<commit_before>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package storage // import "miniflux.app/storage"

import (
	"fmt"
	"strings"
	"time"

	"github.com/lib/pq"

	"miniflux.app/model"
	"miniflux.app/timezone"
)

// EntryQueryBuilder builds a SQL query to fetch entries.
type EntryQueryBuilder struct {
	store      *Storage
	args       []interface{}
	conditions []string
	order      string
	direction  string
	limit      int
	offset     int
}

// WithSearchQuery adds full-text search query to the condition.
func (e *EntryQueryBuilder) WithSearchQuery(query string) *EntryQueryBuilder {
	if query != "" {
		nArgs := len(e.args) + 1
		e.conditions = append(e.conditions, fmt.Sprintf("e.document_vectors @@ plainto_tsquery($%d)", nArgs))
		e.args = append(e.args, query)
		e.WithOrder(fmt.Sprintf("ts_rank(document_vectors, plainto_tsquery($%d))", nArgs))
		e.WithDirection("DESC")
	}
	return e
}

// WithStarred adds starred filter.
func (e *EntryQueryBuilder) WithStarred() *EntryQueryBuilder {
	e.conditions = append(e.conditions, "e.starred is true")
	return e
}

// BeforeDate adds a condition < published_at
func (e *EntryQueryBuilder) BeforeDate(date time.Time) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.published_at < $%d", len(e.args)+1))
	e.args = append(e.args, date)
	return e
}

// AfterDate adds a condition > published_at
func (e *EntryQueryBuilder) AfterDate(date time.Time) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.published_at > $%d", len(e.args)+1))
	e.args = append(e.args, date)
	return e
}

// BeforeEntryID adds a condition < entryID.
func (e *EntryQueryBuilder) BeforeEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id < $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// AfterEntryID adds a condition > entryID.
func (e *EntryQueryBuilder) AfterEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id > $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// WithEntryIDs adds a condition to fetch only the given entry IDs.
func (e *EntryQueryBuilder) WithEntryIDs(entryIDs []int64) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.id = ANY($%d)", len(e.args)+1))
	e.args = append(e.args, pq.Array(entryIDs))
	return e
}

// WithEntryID set the entryID.
func (e *EntryQueryBuilder) WithEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id = $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// WithFeedID set the feedID.
func (e *EntryQueryBuilder) WithFeedID(feedID int64) *EntryQueryBuilder {
	if feedID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.feed_id = $%d", len(e.args)+1))
		e.args = append(e.args, feedID)
	}
	return e
}

// WithCategoryID set the categoryID.
func (e *EntryQueryBuilder) WithCategoryID(categoryID int64) *EntryQueryBuilder {
	if categoryID > 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("f.category_id = $%d", len(e.args)+1))
		e.args = append(e.args, categoryID)
	}
	return e
}

// WithStatus set the entry status.
func (e *EntryQueryBuilder) WithStatus(status string) *EntryQueryBuilder {
	if status != "" {
		e.conditions = append(e.conditions, fmt.Sprintf("e.status = $%d", len(e.args)+1))
		e.args = append(e.args, status)
	}
	return e
}

// WithoutStatus set the entry status that should not be returned.
func (e *EntryQueryBuilder) WithoutStatus(status string) *EntryQueryBuilder {
	if status != "" {
		e.conditions = append(e.conditions, fmt.Sprintf("e.status <> $%d", len(e.args)+1))
		e.args = append(e.args, status)
	}
	return e
}

// WithShareCode set the entry share code.
func (e *EntryQueryBuilder) WithShareCode(shareCode string) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.share_code = $%d", len(e.args)+1))
	e.args = append(e.args, shareCode)
	return e
}

// WithShareCodeNotEmpty adds a filter for non-empty share code.
func (e *EntryQueryBuilder) WithShareCodeNotEmpty() *EntryQueryBuilder {
	e.conditions = append(e.conditions, "e.share_code <> ''")
	return e
}

// WithOrder set the sorting order.
func (e *EntryQueryBuilder) WithOrder(order string) *EntryQueryBuilder {
	e.order = order
	return e
}

// WithDirection set the sorting direction.
func (e *EntryQueryBuilder) WithDirection(direction string) *EntryQueryBuilder {
	e.direction = direction
	return e
}

// WithLimit set the limit.
func (e *EntryQueryBuilder) WithLimit(limit int) *EntryQueryBuilder {
	e.limit = limit
	return e
}

// WithOffset set the offset.
func (e *EntryQueryBuilder) WithOffset(offset int) *EntryQueryBuilder {
	e.offset = offset
	return e
}

// CountEntries count the number of entries that match the condition.
func (e *EntryQueryBuilder) CountEntries() (count int, err error) {
	query := `SELECT count(*) FROM entries e LEFT JOIN feeds f ON f.id=e.feed_id WHERE %s`
	condition := e.buildCondition()

	err = e.store.db.QueryRow(fmt.Sprintf(query, condition), e.args...).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("unable to count entries: %v", err)
	}

	return count, nil
}

// GetEntry returns a single entry that match the condition.
func (e *EntryQueryBuilder) GetEntry() (*model.Entry, error) {
	e.limit = 1
	entries, err := e.GetEntries()
	if err != nil {
		return nil, err
	}

	if len(entries) != 1 {
		return nil, nil
	}

	entries[0].Enclosures, err = e.store.GetEnclosures(entries[0].ID)
	if err != nil {
		return nil, err
	}

	return entries[0], nil
}

// GetEntries returns a list of entries that match the condition.
func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
	query := `
		SELECT
			e.id,
			e.user_id,
			e.feed_id,
			e.hash,
			e.published_at at time zone u.timezone,
			e.title,
			e.url,
			e.comments_url,
			e.author,
			e.share_code,
			e.content,
			e.status,
			e.starred,
			f.title as feed_title,
			f.feed_url,
			f.site_url,
			f.checked_at,
			f.category_id, c.title as category_title,
			f.scraper_rules,
			f.rewrite_rules,
			f.crawler,
			f.user_agent,
			fi.icon_id,
			u.timezone
		FROM
			entries e
		LEFT JOIN
			feeds f ON f.id=e.feed_id
		LEFT JOIN
			categories c ON c.id=f.category_id
		LEFT JOIN
			feed_icons fi ON fi.feed_id=f.id
		LEFT JOIN
			users u ON u.id=e.user_id
		WHERE %s %s
	`

	condition := e.buildCondition()
	sorting := e.buildSorting()
	query = fmt.Sprintf(query, condition, sorting)

	rows, err := e.store.db.Query(query, e.args...)
	if err != nil {
		return nil, fmt.Errorf("unable to get entries: %v", err)
	}
	defer rows.Close()

	entries := make(model.Entries, 0)
	for rows.Next() {
		var entry model.Entry
		var iconID interface{}
		var tz string

		entry.Feed = &model.Feed{}
		entry.Feed.Category = &model.Category{}
		entry.Feed.Icon = &model.FeedIcon{}

		err := rows.Scan(
			&entry.ID,
			&entry.UserID,
			&entry.FeedID,
			&entry.Hash,
			&entry.Date,
			&entry.Title,
			&entry.URL,
			&entry.CommentsURL,
			&entry.Author,
			&entry.ShareCode,
			&entry.Content,
			&entry.Status,
			&entry.Starred,
			&entry.Feed.Title,
			&entry.Feed.FeedURL,
			&entry.Feed.SiteURL,
			&entry.Feed.CheckedAt,
			&entry.Feed.Category.ID,
			&entry.Feed.Category.Title,
			&entry.Feed.ScraperRules,
			&entry.Feed.RewriteRules,
			&entry.Feed.Crawler,
			&entry.Feed.UserAgent,
			&iconID,
			&tz,
		)

		if err != nil {
			return nil, fmt.Errorf("unable to fetch entry row: %v", err)
		}

		if iconID == nil {
			entry.Feed.Icon.IconID = 0
		} else {
			entry.Feed.Icon.IconID = iconID.(int64)
		}

		// Make sure that timestamp fields contains timezone information (API)
		entry.Date = timezone.Convert(tz, entry.Date)
		entry.Feed.CheckedAt = timezone.Convert(tz, entry.Feed.CheckedAt)

		entry.Feed.ID = entry.FeedID
		entry.Feed.UserID = entry.UserID
		entry.Feed.Icon.FeedID = entry.FeedID
		entry.Feed.Category.UserID = entry.UserID
		entries = append(entries, &entry)
	}

	return entries, nil
}

// GetEntryIDs returns a list of entry IDs that match the condition.
func (e *EntryQueryBuilder) GetEntryIDs() ([]int64, error) {
	query := `SELECT e.id FROM entries e LEFT JOIN feeds f ON f.id=e.feed_id WHERE %s %s`

	condition := e.buildCondition()
	query = fmt.Sprintf(query, condition, e.buildSorting())

	rows, err := e.store.db.Query(query, e.args...)
	if err != nil {
		return nil, fmt.Errorf("unable to get entries: %v", err)
	}
	defer rows.Close()

	var entryIDs []int64
	for rows.Next() {
		var entryID int64

		err := rows.Scan(&entryID)
		if err != nil {
			return nil, fmt.Errorf("unable to fetch entry row: %v", err)
		}

		entryIDs = append(entryIDs, entryID)
	}

	return entryIDs, nil
}

func (e *EntryQueryBuilder) buildCondition() string {
	return strings.Join(e.conditions, " AND ")
}

func (e *EntryQueryBuilder) buildSorting() string {
	var parts []string

	if e.order != "" {
		parts = append(parts, fmt.Sprintf(`ORDER BY %s`, e.order))
	}

	if e.direction != "" {
		parts = append(parts, fmt.Sprintf(`%s`, e.direction))
	}

	if e.limit != 0 {
		parts = append(parts, fmt.Sprintf(`LIMIT %d`, e.limit))
	}

	if e.offset != 0 {
		parts = append(parts, fmt.Sprintf(`OFFSET %d`, e.offset))
	}

	return strings.Join(parts, " ")
}

// NewEntryQueryBuilder returns a new EntryQueryBuilder.
func NewEntryQueryBuilder(store *Storage, userID int64) *EntryQueryBuilder {
	return &EntryQueryBuilder{
		store:      store,
		args:       []interface{}{userID},
		conditions: []string{"e.user_id = $1"},
	}
}

// NewAnonymousQueryBuilder returns a new EntryQueryBuilder suitable for anonymous users.
func NewAnonymousQueryBuilder(store *Storage) *EntryQueryBuilder {
	return &EntryQueryBuilder{
		store: store,
	}
}
<commit_msg>Display recent entries first in search results<commit_after>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package storage // import "miniflux.app/storage"

import (
	"fmt"
	"strings"
	"time"

	"github.com/lib/pq"

	"miniflux.app/model"
	"miniflux.app/timezone"
)

// EntryQueryBuilder builds a SQL query to fetch entries.
type EntryQueryBuilder struct {
	store      *Storage
	args       []interface{}
	conditions []string
	order      string
	direction  string
	limit      int
	offset     int
}

// WithSearchQuery adds full-text search query to the condition.
func (e *EntryQueryBuilder) WithSearchQuery(query string) *EntryQueryBuilder {
	if query != "" {
		nArgs := len(e.args) + 1
		e.conditions = append(e.conditions, fmt.Sprintf("e.document_vectors @@ plainto_tsquery($%d)", nArgs))
		e.args = append(e.args, query)

		// 0.0000001 = 0.1 / (seconds_in_a_day)
		e.WithOrder(fmt.Sprintf("ts_rank(document_vectors, plainto_tsquery($%d)) - extract (epoch from now() - published_at)::float * 0.0000001", nArgs))
		e.WithDirection("DESC")
	}
	return e
}

// WithStarred adds starred filter.
func (e *EntryQueryBuilder) WithStarred() *EntryQueryBuilder {
	e.conditions = append(e.conditions, "e.starred is true")
	return e
}

// BeforeDate adds a condition < published_at
func (e *EntryQueryBuilder) BeforeDate(date time.Time) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.published_at < $%d", len(e.args)+1))
	e.args = append(e.args, date)
	return e
}

// AfterDate adds a condition > published_at
func (e *EntryQueryBuilder) AfterDate(date time.Time) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.published_at > $%d", len(e.args)+1))
	e.args = append(e.args, date)
	return e
}

// BeforeEntryID adds a condition < entryID.
func (e *EntryQueryBuilder) BeforeEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id < $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// AfterEntryID adds a condition > entryID.
func (e *EntryQueryBuilder) AfterEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id > $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// WithEntryIDs adds a condition to fetch only the given entry IDs.
func (e *EntryQueryBuilder) WithEntryIDs(entryIDs []int64) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.id = ANY($%d)", len(e.args)+1))
	e.args = append(e.args, pq.Array(entryIDs))
	return e
}

// WithEntryID set the entryID.
func (e *EntryQueryBuilder) WithEntryID(entryID int64) *EntryQueryBuilder {
	if entryID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.id = $%d", len(e.args)+1))
		e.args = append(e.args, entryID)
	}
	return e
}

// WithFeedID set the feedID.
func (e *EntryQueryBuilder) WithFeedID(feedID int64) *EntryQueryBuilder {
	if feedID != 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("e.feed_id = $%d", len(e.args)+1))
		e.args = append(e.args, feedID)
	}
	return e
}

// WithCategoryID set the categoryID.
func (e *EntryQueryBuilder) WithCategoryID(categoryID int64) *EntryQueryBuilder {
	if categoryID > 0 {
		e.conditions = append(e.conditions, fmt.Sprintf("f.category_id = $%d", len(e.args)+1))
		e.args = append(e.args, categoryID)
	}
	return e
}

// WithStatus set the entry status.
func (e *EntryQueryBuilder) WithStatus(status string) *EntryQueryBuilder {
	if status != "" {
		e.conditions = append(e.conditions, fmt.Sprintf("e.status = $%d", len(e.args)+1))
		e.args = append(e.args, status)
	}
	return e
}

// WithoutStatus set the entry status that should not be returned.
func (e *EntryQueryBuilder) WithoutStatus(status string) *EntryQueryBuilder {
	if status != "" {
		e.conditions = append(e.conditions, fmt.Sprintf("e.status <> $%d", len(e.args)+1))
		e.args = append(e.args, status)
	}
	return e
}

// WithShareCode set the entry share code.
func (e *EntryQueryBuilder) WithShareCode(shareCode string) *EntryQueryBuilder {
	e.conditions = append(e.conditions, fmt.Sprintf("e.share_code = $%d", len(e.args)+1))
	e.args = append(e.args, shareCode)
	return e
}

// WithShareCodeNotEmpty adds a filter for non-empty share code.
func (e *EntryQueryBuilder) WithShareCodeNotEmpty() *EntryQueryBuilder {
	e.conditions = append(e.conditions, "e.share_code <> ''")
	return e
}

// WithOrder set the sorting order.
func (e *EntryQueryBuilder) WithOrder(order string) *EntryQueryBuilder {
	e.order = order
	return e
}

// WithDirection set the sorting direction.
func (e *EntryQueryBuilder) WithDirection(direction string) *EntryQueryBuilder {
	e.direction = direction
	return e
}

// WithLimit set the limit.
func (e *EntryQueryBuilder) WithLimit(limit int) *EntryQueryBuilder {
	e.limit = limit
	return e
}

// WithOffset set the offset.
func (e *EntryQueryBuilder) WithOffset(offset int) *EntryQueryBuilder {
	e.offset = offset
	return e
}

// CountEntries count the number of entries that match the condition.
func (e *EntryQueryBuilder) CountEntries() (count int, err error) {
	query := `SELECT count(*) FROM entries e LEFT JOIN feeds f ON f.id=e.feed_id WHERE %s`
	condition := e.buildCondition()

	err = e.store.db.QueryRow(fmt.Sprintf(query, condition), e.args...).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("unable to count entries: %v", err)
	}

	return count, nil
}

// GetEntry returns a single entry that match the condition.
func (e *EntryQueryBuilder) GetEntry() (*model.Entry, error) {
	e.limit = 1
	entries, err := e.GetEntries()
	if err != nil {
		return nil, err
	}

	if len(entries) != 1 {
		return nil, nil
	}

	entries[0].Enclosures, err = e.store.GetEnclosures(entries[0].ID)
	if err != nil {
		return nil, err
	}

	return entries[0], nil
}

// GetEntries returns a list of entries that match the condition.
func (e *EntryQueryBuilder) GetEntries() (model.Entries, error) {
	query := `
		SELECT
			e.id,
			e.user_id,
			e.feed_id,
			e.hash,
			e.published_at at time zone u.timezone,
			e.title,
			e.url,
			e.comments_url,
			e.author,
			e.share_code,
			e.content,
			e.status,
			e.starred,
			f.title as feed_title,
			f.feed_url,
			f.site_url,
			f.checked_at,
			f.category_id, c.title as category_title,
			f.scraper_rules,
			f.rewrite_rules,
			f.crawler,
			f.user_agent,
			fi.icon_id,
			u.timezone
		FROM
			entries e
		LEFT JOIN
			feeds f ON f.id=e.feed_id
		LEFT JOIN
			categories c ON c.id=f.category_id
		LEFT JOIN
			feed_icons fi ON fi.feed_id=f.id
		LEFT JOIN
			users u ON u.id=e.user_id
		WHERE %s %s
	`

	condition := e.buildCondition()
	sorting := e.buildSorting()
	query = fmt.Sprintf(query, condition, sorting)

	rows, err := e.store.db.Query(query, e.args...)
	if err != nil {
		return nil, fmt.Errorf("unable to get entries: %v", err)
	}
	defer rows.Close()

	entries := make(model.Entries, 0)
	for rows.Next() {
		var entry model.Entry
		var iconID interface{}
		var tz string

		entry.Feed = &model.Feed{}
		entry.Feed.Category = &model.Category{}
		entry.Feed.Icon = &model.FeedIcon{}

		err := rows.Scan(
			&entry.ID,
			&entry.UserID,
			&entry.FeedID,
			&entry.Hash,
			&entry.Date,
			&entry.Title,
			&entry.URL,
			&entry.CommentsURL,
			&entry.Author,
			&entry.ShareCode,
			&entry.Content,
			&entry.Status,
			&entry.Starred,
			&entry.Feed.Title,
			&entry.Feed.FeedURL,
			&entry.Feed.SiteURL,
			&entry.Feed.CheckedAt,
			&entry.Feed.Category.ID,
			&entry.Feed.Category.Title,
			&entry.Feed.ScraperRules,
			&entry.Feed.RewriteRules,
			&entry.Feed.Crawler,
			&entry.Feed.UserAgent,
			&iconID,
			&tz,
		)

		if err != nil {
			return nil, fmt.Errorf("unable to fetch entry row: %v", err)
		}

		if iconID == nil {
			entry.Feed.Icon.IconID = 0
		} else {
			entry.Feed.Icon.IconID = iconID.(int64)
		}

		// Make sure that timestamp fields contains timezone information (API)
		entry.Date = timezone.Convert(tz, entry.Date)
		entry.Feed.CheckedAt = timezone.Convert(tz, entry.Feed.CheckedAt)

		entry.Feed.ID = entry.FeedID
		entry.Feed.UserID = entry.UserID
		entry.Feed.Icon.FeedID = entry.FeedID
		entry.Feed.Category.UserID = entry.UserID
		entries = append(entries, &entry)
	}

	return entries, nil
}

// GetEntryIDs returns a list of entry IDs that match the condition.
func (e *EntryQueryBuilder) GetEntryIDs() ([]int64, error) {
	query := `SELECT e.id FROM entries e LEFT JOIN feeds f ON f.id=e.feed_id WHERE %s %s`

	condition := e.buildCondition()
	query = fmt.Sprintf(query, condition, e.buildSorting())

	rows, err := e.store.db.Query(query, e.args...)
	if err != nil {
		return nil, fmt.Errorf("unable to get entries: %v", err)
	}
	defer rows.Close()

	var entryIDs []int64
	for rows.Next() {
		var entryID int64

		err := rows.Scan(&entryID)
		if err != nil {
			return nil, fmt.Errorf("unable to fetch entry row: %v", err)
		}

		entryIDs = append(entryIDs, entryID)
	}

	return entryIDs, nil
}

func (e *EntryQueryBuilder) buildCondition() string {
	return strings.Join(e.conditions, " AND ")
}

func (e *EntryQueryBuilder) buildSorting() string {
	var parts []string

	if e.order != "" {
		parts = append(parts, fmt.Sprintf(`ORDER BY %s`, e.order))
	}

	if e.direction != "" {
		parts = append(parts, fmt.Sprintf(`%s`, e.direction))
	}

	if e.limit != 0 {
		parts = append(parts, fmt.Sprintf(`LIMIT %d`, e.limit))
	}

	if e.offset != 0 {
		parts = append(parts, fmt.Sprintf(`OFFSET %d`, e.offset))
	}

	return strings.Join(parts, " ")
}

// NewEntryQueryBuilder returns a new EntryQueryBuilder.
func NewEntryQueryBuilder(store *Storage, userID int64) *EntryQueryBuilder {
	return &EntryQueryBuilder{
		store:      store,
		args:       []interface{}{userID},
		conditions: []string{"e.user_id = $1"},
	}
}

// NewAnonymousQueryBuilder returns a new EntryQueryBuilder suitable for anonymous users.
func NewAnonymousQueryBuilder(store *Storage) *EntryQueryBuilder {
	return &EntryQueryBuilder{
		store: store,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"

	"addr"
	"netorder"
)

func main() {

	if len(os.Args) < 3 {
		fmt.Printf("usage:   rip-query host:port     net1 [ net2  ... netN ]\n")
		fmt.Printf("example: rip-query 224.0.0.9:520 1.0.0.0/24       2.0.0.0/24\n")
		return
	}

	query(os.Args[1], os.Args[2:])
}

func query(hostPort string, nets []string) {

	entries := len(nets)
	bufSize := 4 + 20*entries
	buf := make([]byte, bufSize, bufSize)

	buf[0] = 1 // rip request
	buf[1] = 2 // rip version

	for i, n := range nets {
		_, netaddr, err := net.ParseCIDR(n)
		if err != nil {
			fmt.Printf("could not solve network: '%s': %v\n", n, err)
			return
		}

		offset := 4 + 20*i
		netorder.WriteUint16(buf, offset, 2)   // family=AF_INET
		netorder.WriteUint16(buf, offset+2, 0) // route tag
		addr.WriteIPv4(buf, offset+4, netaddr.IP)
		addr.WriteIPv4Mask(buf, offset+8, netaddr.Mask)
		addr.WriteIPv4(buf, offset+12, net.IPv4(0, 0, 0, 0))
		netorder.WriteUint32(buf, offset+16, 0) // metric
	}

	proto := "udp"

	raddr, err := net.ResolveUDPAddr(proto, hostPort)
	if err != nil {
		fmt.Printf("could not solve udp endpoint: '%s': %v\n", hostPort, err)
		return
	}

	conn, err := net.DialUDP(proto, nil, raddr)
	if err != nil {
		fmt.Printf("could not create connection for remote endpoint: %v: %v\n", raddr, err)
		return
	}

	n, err := conn.Write(buf)
	if err != nil {
		fmt.Printf("could not send rip dgram: size=%d to %v: %v\n", len(buf), raddr, err)
		return
	}
	if n != len(buf) {
		fmt.Printf("partil write rip dgram: sent=%d size=%d to %v: %v\n", n, len(buf), raddr, err)
		return
	}

	fmt.Printf("sent rip dgram: size=%d to %v\n", len(buf), raddr)
}
<commit_msg>RIP query tool support for whole-table requests.<commit_after>package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"

	"addr"
	"netorder"
)

func main() {

	if len(os.Args) < 3 {
		fmt.Printf("usage:   rip-query host:port     net1 [ net2  ... netN ]\n")
		fmt.Printf("example: rip-query 224.0.0.9:520 1.0.0.0/24       2.0.0.0/24\n")
		fmt.Printf("example: rip-query 224.0.0.9:520 0.0.0.0/0,0\n")
		return
	}

	query(os.Args[1], os.Args[2:])
}

func query(hostPort string, nets []string) {

	entries := len(nets)
	bufSize := 4 + 20*entries
	buf := make([]byte, bufSize, bufSize)

	buf[0] = 1 // rip request
	buf[1] = 2 // rip version

	for i, n := range nets {
		family := uint16(2) // AF_INET
		f := strings.Split(n, ",")
		if len(f) > 1 {
			af, err := strconv.Atoi(f[1])
			if err == nil {
				family = uint16(af)
				n = f[0]
			} else {
				fmt.Printf("could not solve address family: '%s': %v\n", n, err)
				return
			}
		}

		_, netaddr, err := net.ParseCIDR(n)
		if err != nil {
			fmt.Printf("could not solve network: '%s': %v\n", n, err)
			return
		}

		offset := 4 + 20*i
		netorder.WriteUint16(buf, offset, family)
		netorder.WriteUint16(buf, offset+2, 0) // route tag
		addr.WriteIPv4(buf, offset+4, netaddr.IP)
		addr.WriteIPv4Mask(buf, offset+8, netaddr.Mask)
		addr.WriteIPv4(buf, offset+12, net.IPv4(0, 0, 0, 0))
		netorder.WriteUint32(buf, offset+16, 16) // metric
	}

	proto := "udp"

	raddr, err := net.ResolveUDPAddr(proto, hostPort)
	if err != nil {
		fmt.Printf("could not solve udp endpoint: '%s': %v\n", hostPort, err)
		return
	}

	conn, err := net.DialUDP(proto, nil, raddr)
	if err != nil {
		fmt.Printf("could not create connection for remote endpoint: %v: %v\n", raddr, err)
		return
	}

	n, err := conn.Write(buf)
	if err != nil {
		fmt.Printf("could not send rip dgram: size=%d to %v: %v\n", len(buf), raddr, err)
		return
	}
	if n != len(buf) {
		fmt.Printf("partil write rip dgram: sent=%d size=%d to %v: %v\n", n, len(buf), raddr, err)
		return
	}

	fmt.Printf("sent rip dgram: size=%d to %v\n", len(buf), raddr)
}
<|endoftext|>


<commit_before>package linker

import(
	tp "tritium/proto"
	proto "goprotobuf.googlecode.com/hg/proto"
	. "tritium/packager"
)

type Executable struct {
	*tp.Executable
}

func NewExecutable(pkg *Package) (*Executable){
	exec := &Executable{
		Executable: &tp.Executable{
			Pkg: pkg.Package,
		},
	}
	return exec
}

func (exec *Executable) ProcessObjects(objs []*tp.ScriptObject) {
	// Add script objects to the exec
	exec.Objects = objs

	// Loop through all the objects, and get the index of every
	// script object name.
	objScriptNameLookupMap := make(map[string]int, 0)
	for objIndex, obj := range(exec.Objects) {
		objScriptNameLookupMap[proto.GetString(obj.Name)] = objIndex
	}

	for _, obj := range(exec.Objects) {
		instructionList := make([]*tp.Instruction, 0)
		instructionList = append(instructionList, obj.Root)

		/*
			This is how I am currently looping through all of the
			instructions. The array above is going to end up being
			an array of *every* instruction. Pushing and popping
			is probably a better idea, but I don't have access to the
			Golang docs right now, so this is my solution. I don't believe
			it will be that slow to do it this way, but feel free to
			change this code to be more efficient in the future.
		*/
		for i := 0; i < len(instructionList); i++ {
			ins := instructionList[i]
			if ins.Children != nil {
				for _, child := range(ins.Children) {
					instructionList = append(instructionList, child)
				}
			}
			// Grab all imports
			if *ins.Type == tp.Instruction_IMPORT {
				// set its import_id and blank the value field
				importValue := proto.GetString(ins.Value)
				println("Found import!", importValue)
				println("Index is...", objScriptNameLookupMap[importValue])
			}
		}
	}

	// t.ProcessInstructions()
		// Figure out function signature (name + arg types)
			// have to start at the bottom of the tree (args first) and check types.
		// Is this a real function?
			// aka, text(regexp()) ... have to see that regexp returns Regexp object,
			// which, then, when we go to process text() we notice we don't have a text(Regexp)
			// function, so we need to throw a reasonable error
			// Hrrrm.... need line numbers, huh?
		// Set the function_id if it is real, error otherwise
	// optionally, remove functions from pkg that aren't used (dead code removal)
}
<commit_msg>rework the inline comments that explain what needs to happen<commit_after>package linker

import(
	tp "tritium/proto"
	proto "goprotobuf.googlecode.com/hg/proto"
	. "tritium/packager"
)

type Executable struct {
	*tp.Executable
}

func NewExecutable(pkg *Package) (*Executable){
	exec := &Executable{
		Executable: &tp.Executable{
			Pkg: pkg.Package,
		},
	}
	return exec
}

func (exec *Executable) ProcessObjects(objs []*tp.ScriptObject) {
	// Add script objects to the exec
	exec.Objects = objs

	// Loop through all the objects, and get the index of every
	// script object name.
	objScriptNameLookupMap := make(map[string]int, 0)
	for objIndex, obj := range(exec.Objects) {
		objScriptNameLookupMap[proto.GetString(obj.Name)] = objIndex
	}

	for _, obj := range(exec.Objects) {
		instructionList := make([]*tp.Instruction, 0)
		instructionList = append(instructionList, obj.Root)

		/*
			This is how I am currently looping through all of the
			instructions. The array above is going to end up being
			an array of *every* instruction. Pushing and popping
			is probably a better idea, but I don't have access to the
			Golang docs right now, so this is my solution. I don't believe
			it will be that slow to do it this way, but feel free to
			change this code to be more efficient in the future.
		*/
		for i := 0; i < len(instructionList); i++ {
			ins := instructionList[i]
			if ins.Children != nil {
				for _, child := range(ins.Children) {
					instructionList = append(instructionList, child)
				}
			}
			// Grab all imports
			if *ins.Type == tp.Instruction_IMPORT {
				// set its import_id and blank the value field
				importValue := proto.GetString(ins.Value)
				println("Found import!", importValue)
				println("Index is...", objScriptNameLookupMap[importValue])
			}
			// if function
				// Figure out function signature (name + arg types)
					// have to start at the bottom of the tree (args first) and check types.
				// Is this a real function?
					// aka, text(regexp()) ... have to see that regexp returns Regexp object,
					// which, then, when we go to process text() we notice we don't have a text(Regexp)
					// function, so we need to throw a reasonable error
					// Hrrrm.... need line numbers, huh?
				// Set the function_id if it is real, error otherwise
		}
	}
	// optionally, remove functions from pkg that aren't used (dead code removal)
}
<|endoftext|>


<commit_before><commit_msg>Fix some bugs in CRC8<commit_after><|endoftext|>


<commit_before>package rtsengine

import (
	"container/list"
	"encoding/json"
	"fmt"
	"image"
	"net"
)

// Game is an actual game with UDP ports and IPlayers
// In theory the rtsengine can maintain N number of simultaneous
// running Games as long as UDP ports do not overlap.
type Game struct {
	// Description of game
	Description string

	// Our players for this game.
	// Once the game begins this array does not change.
	Players []IPlayer

	// The world map that maintains the terrain and units.
	OurWorld *World

	// The automated mechanics of this particular game.
	Mechanics []IMechanic

	// Our master pool for frequently used items
	ItemPool *Pool

	// Pathing systems
	Pathing *AStarPathing

	// Command channel
	CommandChannel chan *WirePacket
}

// NewGame constructs a new game according to the parameters.
func NewGame(
	description string,

	// How many items to pool for decreased GC
	poolItems int,

	noOfHumanPlayers int,
	noOfAIPlayers int,

	// Width and Height of Player View
	playerViewWidth int, playerViewHeight int,

	// Width and Height in Acres of our world.
	worldWidth int, worldHeight int) (*Game, error) {

	// This instance
	game := Game{}

	// Item Pool
	game.ItemPool = &Pool{}
	game.ItemPool.Generate(poolItems)

	// Instantiate the pathing system
	game.Pathing = &AStarPathing{}

	// The command channel that accepts WirePacket commands
	// and performs the necessary operation.
	game.CommandChannel = make(chan *WirePacket, 500)

	// Used for display so we have some idea what games are being played.
	// Make this very descriptive and long. Like '4 Human Players, Fog of War, World(500,500)'
	game.Description = description

	// Instantiate the world
	game.OurWorld = NewWorld(worldWidth, worldHeight)

	// Generate a world. Fill it with trees and rivers and ...
	game.OurWorld.GenerateSimple()

	// Create Players
	game.Players = make([]IPlayer, noOfAIPlayers+noOfHumanPlayers)

	// Situate player bases onto the world without overlapping.
	rects, error := game.SituateHomeBases(noOfAIPlayers+noOfHumanPlayers, playerViewWidth, playerViewHeight)

	if error != nil {
		return nil, fmt.Errorf("Failed to situate home bases into world grid. Please reduce number of players and/or increase world size")
	}

	// Create Human Players
	i := 0
	for ; i < noOfHumanPlayers; i++ {
		// The world point needs to be inserted into a random location
		game.Players[i] = NewHumanPlayer(fmt.Sprintf("Human Player %d", i), rects[i].Min, playerViewWidth, playerViewHeight, game.ItemPool, game.Pathing, game.OurWorld)
		game.GenerateUnits(game.Players[i])
	}

	// Create Machine Intelligent Players
	for j := 0; j < noOfAIPlayers; j++ {
		// The world point needs to be inserted into a random location
		game.Players[i] = NewAIPlayer(fmt.Sprintf("AI Player %d", j), rects[i].Min, playerViewWidth, playerViewHeight, game.ItemPool, game.Pathing, game.OurWorld)
		i++
		game.GenerateUnits(game.Players[i])
	}

	// Add mechanics
	movemech := NewMovementMechanic(game.OurWorld, game.CommandChannel, game.Players, game.Pathing, &game)
	game.Mechanics = make([]IMechanic, 1)
	game.Mechanics[0] = movemech

	return &game, nil
}

// AcceptNetConnections will accept connections from UI's (humans presumably) and
// assign them a player. Once all humanplayers are accepted this method returns
// WITHOUT starting the game. We are waiting at this point ready to go.
func (game *Game) AcceptNetConnections(host string, port int) error {

	for !game.ReadyToGo() {
		// listen to incoming tcp connections
		listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
		if err != nil {
			return err
		}

		// Accept and if successful assign to player
		conn, err := listener.Accept()
		if err != nil {
			continue
		}

		for _, player := range game.Players {
			if player.isHuman() && !player.isWireAlive() {
				player.listen(&TCPWire{conn, json.NewDecoder(conn), json.NewEncoder(conn)})
				break
			}
		}
	}

	return nil
}

// SituateHomeBases will construct home bases in the proper
// locations on the world. That is within the world but not overlapping one another.
// It's possible for large numbers of players on a too small grid this heuristic will not converge
// and an error will be returned.
func (game *Game) SituateHomeBases(noOfPlayers int, playerViewWidth int, playerViewHeight int) ([]*image.Rectangle, error) {
	playerRects := make([]*image.Rectangle, noOfPlayers)

	//s1 := rand.NewSource(time.Now().UnixNano())
	//r1 := rand.New(s1)

OUTER:
	for i, j := 0, 0; i < noOfPlayers; j++ {

		// No convergence?
		if j >= 1000 {
			return nil, fmt.Errorf("Not enough space in world grid to insert player grids.")
		}

		// Random point within the world
		//randomRect := image.Rect(r1.Intn(game.OurWorld.Span.Dx()), r1.Intn(game.OurWorld.Span.Dy()), playerViewHeight, playerViewWidth)
		randomRect := image.Rect(0, 0, playerViewHeight, playerViewWidth)

		// If no players yet just add it and continue.
		if i == 0 {
			playerRects[i] = &randomRect
			i++
			continue
		}

		// Ensure no overlaps with existing player rects
		for _, r := range playerRects {
			// End of array.
			if r == nil {
				break
			}

			// two player home grids overlap. Try again...
			if r.Overlaps(randomRect) {
				continue OUTER
			}
		}

		// no overlap!
		playerRects[i] = &randomRect
		i++
	}

	return playerRects, nil
}

// Start will start the game.
func (game *Game) Start() {

	// List for command on the command channel
	go game.CommandChannelHandler()

	for _, mech := range game.Mechanics {
		go mech.start()
	}

	for _, player := range game.Players {
		err := player.start()
		if err != nil {
			fmt.Printf("Player %s has failed", player.name())
			// Return this error?
		}
	}
}

// Stop will stop the game.
func (game *Game) Stop() {
	close(game.CommandChannel)

	for _, player := range game.Players {
		player.stop()
	}

	for _, mechanic := range game.Mechanics {
		mechanic.stop()
	}

}

// ReadyToGo returns true if we are ready to start a game.
func (game *Game) ReadyToGo() bool {

	// Essentially check if all human players are ready to go.
	// AI's are always ready.
	for _, player := range game.Players {
		if player.isHuman() && !player.isWireAlive() {
			return false
		}
	}

	return true
}

// FindPath finds a path from source to destination within this game's world and return it as a list of Waypoints
func (game *Game) FindPath(source *image.Point, destination *image.Point) (*list.List, error) {
	return game.Pathing.FindPath(game.ItemPool, &game.OurWorld.Grid, source, destination)
}

//FreeList will free the list return by FindPath
func (game *Game) FreeList(l *list.List) {
	game.Pathing.FreeList(game.ItemPool, l)
}

// GenerateUnits will construct the starting units per player.
func (game *Game) GenerateUnits(player IPlayer) {

	// Need general information about our grid and our view projection onto the grid.
	view := player.PlayerView()
	viewCenter := view.Center()
	worldCenter := view.ToWorldPoint(&viewCenter)

	/////////////////////////////////////////////////////////////////////////
	//           HomeStead is special. Only one in center location         //
	/////////////////////////////////////////////////////////////////////////
	homestead := HomeStead{}
	homestead.generate(player)

	err := game.OurWorld.Add(&homestead, &worldCenter)
	if err != nil {
		fmt.Print(err)
	}
	homestead.CurrentLocation = &worldCenter
	player.PlayerUnits().Add(&homestead)

	/////////////////////////////////////////////////////////////////////////
	//                               All Units                             //
	/////////////////////////////////////////////////////////////////////////
	infantry := game.ItemPool.Infantry(1)
	infantry[0].generate(player)
	game.AddUnitCloseToPoint(player, infantry[0], &viewCenter, 10)

	farm := game.ItemPool.Farms(1)
	farm[0].generate(player)
	game.AddUnitCloseToPoint(player, farm[0], &viewCenter, 10)

	cavalry := game.ItemPool.Cavalry(1)
	cavalry[0].generate(player)
	game.AddUnitCloseToPoint(player, cavalry[0], &viewCenter, 20)

	woodpile := game.ItemPool.Woodpiles(1)
	woodpile[0].generate(player)
	game.AddUnitCloseToPoint(player, woodpile[0], &viewCenter, 30)

	goldmine := game.ItemPool.Goldmines(1)
	goldmine[0].generate(player)
	game.AddUnitCloseToPoint(player, goldmine[0], &viewCenter, 30)

	stonequarry := game.ItemPool.StoneQuarry(1)
	stonequarry[0].generate(player)
	game.AddUnitCloseToPoint(player, stonequarry[0], &viewCenter, 30)

}

// CommandChannelHandler will handle the command channel and dispatch
// the wire packets.
func (game *Game) CommandChannelHandler() {
	for packet := range game.CommandChannel {
		for _, player := range game.Players {
			_ = player.dispatch(packet)
		}
	}
}

// AddUnit will add a unit to this player
// without a collision within the view.
func (game *Game) AddUnit(player IPlayer, unit IUnit) {
	view := player.PlayerView()

	var locus *image.Point
	for {
		locus = view.RandomPointInView()
		if game.OurWorld.In(locus) && !game.OurWorld.Collision(locus) {
			break
		}
	}

	worldLocus := view.ToWorldPoint(locus)

	err := game.OurWorld.Add(unit, &worldLocus)
	if err != nil {
		fmt.Print(err)
	}

	unit.movement().CurrentLocation = &worldLocus

	player.PlayerUnits().Add(unit)
}

// AddUnitCloseToPoint will add unit to player no further than radius away from the central point.
// Will ensure no collisions. Central point is in VIEW coordinates.
func (game *Game) AddUnitCloseToPoint(player IPlayer, unit IUnit, central *image.Point, radius int) {
	view := player.PlayerView()

	var locus *image.Point
	for {
		locus = view.RandomPointClostToPoint(central, radius)
		if game.OurWorld.In(locus) && !game.OurWorld.Collision(locus) {
			break
		}
	}

	worldLocus := view.ToWorldPoint(locus)

	err := game.OurWorld.Add(unit, &worldLocus)
	if err != nil {
		fmt.Print(err)
	}

	unit.movement().CurrentLocation = &worldLocus

	player.PlayerUnits().Add(unit)
}
<commit_msg>Checkpoint. Basic TMX reader. Not doing anything with it yet.<commit_after>package rtsengine

import (
	"container/list"
	"encoding/json"
	"fmt"
	"image"
	"net"
	"os"

	"github.com/salviati/go-tmx/tmx"
)

// Game is an actual game with UDP ports and IPlayers
// In theory the rtsengine can maintain N number of simultaneous
// running Games as long as UDP ports do not overlap.
type Game struct {
	// Description of game
	Description string

	// Our players for this game.
	// Once the game begins this array does not change.
	Players []IPlayer

	// The world map that maintains the terrain and units.
	OurWorld *World

	// The automated mechanics of this particular game.
	Mechanics []IMechanic

	// Our master pool for frequently used items
	ItemPool *Pool

	// Pathing systems
	Pathing *AStarPathing

	// Command channel
	CommandChannel chan *WirePacket
}

// NewGame constructs a new game according to the parameters.
func NewGame(
	description string,

	// How many items to pool for decreased GC
	poolItems int,

	noOfHumanPlayers int,
	noOfAIPlayers int,

	// Width and Height of Player View
	playerViewWidth int, playerViewHeight int,

	// Width and Height in Acres of our world.
	worldWidth int, worldHeight int) (*Game, error) {

	// This instance
	game := Game{}

	// Item Pool
	game.ItemPool = &Pool{}
	game.ItemPool.Generate(poolItems)

	// Instantiate the pathing system
	game.Pathing = &AStarPathing{}

	// The command channel that accepts WirePacket commands
	// and performs the necessary operation.
	game.CommandChannel = make(chan *WirePacket, 500)

	// Used for display so we have some idea what games are being played.
	// Make this very descriptive and long. Like '4 Human Players, Fog of War, World(500,500)'
	game.Description = description

	// Instantiate the world
	game.OurWorld = NewWorld(worldWidth, worldHeight)

	// Generate a world. Fill it with trees and rivers and ...
	game.OurWorld.GenerateSimple()

	// Create Players
	game.Players = make([]IPlayer, noOfAIPlayers+noOfHumanPlayers)

	// Situate player bases onto the world without overlapping.
	rects, error := game.SituateHomeBases(noOfAIPlayers+noOfHumanPlayers, playerViewWidth, playerViewHeight)

	if error != nil {
		return nil, fmt.Errorf("Failed to situate home bases into world grid. Please reduce number of players and/or increase world size")
	}

	// Create Human Players
	i := 0
	for ; i < noOfHumanPlayers; i++ {
		// The world point needs to be inserted into a random location
		game.Players[i] = NewHumanPlayer(fmt.Sprintf("Human Player %d", i), rects[i].Min, playerViewWidth, playerViewHeight, game.ItemPool, game.Pathing, game.OurWorld)
		game.GenerateUnits(game.Players[i])
	}

	// Create Machine Intelligent Players
	for j := 0; j < noOfAIPlayers; j++ {
		// The world point needs to be inserted into a random location
		game.Players[i] = NewAIPlayer(fmt.Sprintf("AI Player %d", j), rects[i].Min, playerViewWidth, playerViewHeight, game.ItemPool, game.Pathing, game.OurWorld)
		i++
		game.GenerateUnits(game.Players[i])
	}

	// Add mechanics
	movemech := NewMovementMechanic(game.OurWorld, game.CommandChannel, game.Players, game.Pathing, &game)
	game.Mechanics = make([]IMechanic, 1)
	game.Mechanics[0] = movemech

	// test tmx
	//_, _ = game.LoadTMX("./tileset/example.tmx")

	return &game, nil
}

// AcceptNetConnections will accept connections from UI's (humans presumably) and
// assign them a player. Once all humanplayers are accepted this method returns
// WITHOUT starting the game. We are waiting at this point ready to go.
func (game *Game) AcceptNetConnections(host string, port int) error {

	for !game.ReadyToGo() {
		// listen to incoming tcp connections
		listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
		if err != nil {
			return err
		}

		// Accept and if successful assign to player
		conn, err := listener.Accept()
		if err != nil {
			continue
		}

		for _, player := range game.Players {
			if player.isHuman() && !player.isWireAlive() {
				player.listen(&TCPWire{conn, json.NewDecoder(conn), json.NewEncoder(conn)})
				break
			}
		}
	}

	return nil
}

// SituateHomeBases will construct home bases in the proper
// locations on the world. That is within the world but not overlapping one another.
// It's possible for large numbers of players on a too small grid this heuristic will not converge
// and an error will be returned.
func (game *Game) SituateHomeBases(noOfPlayers int, playerViewWidth int, playerViewHeight int) ([]*image.Rectangle, error) {
	playerRects := make([]*image.Rectangle, noOfPlayers)

	//s1 := rand.NewSource(time.Now().UnixNano())
	//r1 := rand.New(s1)

OUTER:
	for i, j := 0, 0; i < noOfPlayers; j++ {

		// No convergence?
		if j >= 1000 {
			return nil, fmt.Errorf("Not enough space in world grid to insert player grids.")
		}

		// Random point within the world
		//randomRect := image.Rect(r1.Intn(game.OurWorld.Span.Dx()), r1.Intn(game.OurWorld.Span.Dy()), playerViewHeight, playerViewWidth)
		randomRect := image.Rect(0, 0, playerViewHeight, playerViewWidth)

		// If no players yet just add it and continue.
		if i == 0 {
			playerRects[i] = &randomRect
			i++
			continue
		}

		// Ensure no overlaps with existing player rects
		for _, r := range playerRects {
			// End of array.
			if r == nil {
				break
			}

			// two player home grids overlap. Try again...
			if r.Overlaps(randomRect) {
				continue OUTER
			}
		}

		// no overlap!
		playerRects[i] = &randomRect
		i++
	}

	return playerRects, nil
}

// Start will start the game.
func (game *Game) Start() {

	// List for command on the command channel
	go game.CommandChannelHandler()

	for _, mech := range game.Mechanics {
		go mech.start()
	}

	for _, player := range game.Players {
		err := player.start()
		if err != nil {
			fmt.Printf("Player %s has failed", player.name())
			// Return this error?
		}
	}
}

// Stop will stop the game.
func (game *Game) Stop() {
	close(game.CommandChannel)

	for _, player := range game.Players {
		player.stop()
	}

	for _, mechanic := range game.Mechanics {
		mechanic.stop()
	}

}

// ReadyToGo returns true if we are ready to start a game.
func (game *Game) ReadyToGo() bool {

	// Essentially check if all human players are ready to go.
	// AI's are always ready.
	for _, player := range game.Players {
		if player.isHuman() && !player.isWireAlive() {
			return false
		}
	}

	return true
}

// FindPath finds a path from source to destination within this game's world and return it as a list of Waypoints
func (game *Game) FindPath(source *image.Point, destination *image.Point) (*list.List, error) {
	return game.Pathing.FindPath(game.ItemPool, &game.OurWorld.Grid, source, destination)
}

//FreeList will free the list return by FindPath
func (game *Game) FreeList(l *list.List) {
	game.Pathing.FreeList(game.ItemPool, l)
}

// GenerateUnits will construct the starting units per player.
func (game *Game) GenerateUnits(player IPlayer) {

	// Need general information about our grid and our view projection onto the grid.
	view := player.PlayerView()
	viewCenter := view.Center()
	worldCenter := view.ToWorldPoint(&viewCenter)

	/////////////////////////////////////////////////////////////////////////
	//           HomeStead is special. Only one in center location         //
	/////////////////////////////////////////////////////////////////////////
	homestead := HomeStead{}
	homestead.generate(player)

	err := game.OurWorld.Add(&homestead, &worldCenter)
	if err != nil {
		fmt.Print(err)
	}
	homestead.CurrentLocation = &worldCenter
	player.PlayerUnits().Add(&homestead)

	/////////////////////////////////////////////////////////////////////////
	//                               All Units                             //
	/////////////////////////////////////////////////////////////////////////
	infantry := game.ItemPool.Infantry(1)
	infantry[0].generate(player)
	game.AddUnitCloseToPoint(player, infantry[0], &viewCenter, 10)

	farm := game.ItemPool.Farms(1)
	farm[0].generate(player)
	game.AddUnitCloseToPoint(player, farm[0], &viewCenter, 10)

	cavalry := game.ItemPool.Cavalry(1)
	cavalry[0].generate(player)
	game.AddUnitCloseToPoint(player, cavalry[0], &viewCenter, 20)

	woodpile := game.ItemPool.Woodpiles(1)
	woodpile[0].generate(player)
	game.AddUnitCloseToPoint(player, woodpile[0], &viewCenter, 30)

	goldmine := game.ItemPool.Goldmines(1)
	goldmine[0].generate(player)
	game.AddUnitCloseToPoint(player, goldmine[0], &viewCenter, 30)

	stonequarry := game.ItemPool.StoneQuarry(1)
	stonequarry[0].generate(player)
	game.AddUnitCloseToPoint(player, stonequarry[0], &viewCenter, 30)

}

// CommandChannelHandler will handle the command channel and dispatch
// the wire packets.
func (game *Game) CommandChannelHandler() {
	for packet := range game.CommandChannel {
		for _, player := range game.Players {
			_ = player.dispatch(packet)
		}
	}
}

// AddUnit will add a unit to this player
// without a collision within the view.
func (game *Game) AddUnit(player IPlayer, unit IUnit) {
	view := player.PlayerView()

	var locus *image.Point
	for {
		locus = view.RandomPointInView()
		if game.OurWorld.In(locus) && !game.OurWorld.Collision(locus) {
			break
		}
	}

	worldLocus := view.ToWorldPoint(locus)

	err := game.OurWorld.Add(unit, &worldLocus)
	if err != nil {
		fmt.Print(err)
	}

	unit.movement().CurrentLocation = &worldLocus

	player.PlayerUnits().Add(unit)
}

// AddUnitCloseToPoint will add unit to player no further than radius away from the central point.
// Will ensure no collisions. Central point is in VIEW coordinates.
func (game *Game) AddUnitCloseToPoint(player IPlayer, unit IUnit, central *image.Point, radius int) {
	view := player.PlayerView()

	var locus *image.Point
	for {
		locus = view.RandomPointClostToPoint(central, radius)
		if game.OurWorld.In(locus) && !game.OurWorld.Collision(locus) {
			break
		}
	}

	worldLocus := view.ToWorldPoint(locus)

	err := game.OurWorld.Add(unit, &worldLocus)
	if err != nil {
		fmt.Print(err)
	}

	unit.movement().CurrentLocation = &worldLocus

	player.PlayerUnits().Add(unit)
}

// LoadTMX will load the TMX (XML) file from disk (filename)
// and returns a pointer ot the tmx MAP.
// http://doc.mapeditor.org/reference/tmx-map-format/
func (game *Game) LoadTMX(filename string) (*tmx.Map, error) {

	r, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	m, err := tmx.Read(r)
	if err != nil {
		return nil, err
	}

	return m, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package network

import (
	"encoding/json"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
	"k8s.io/kubernetes/test/images/agnhost/net/nat"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var kubeProxyE2eImage = imageutils.GetE2EImage(imageutils.Agnhost)

var _ = SIGDescribe("Network", func() {
	const (
		testDaemonHTTPPort    = 11301
		testDaemonTCPPort     = 11302
		timeoutSeconds        = 10
		postFinTimeoutSeconds = 5
	)

	fr := framework.NewDefaultFramework("network")

	ginkgo.It("should set TCP CLOSE_WAIT timeout", func() {
		nodes := framework.GetReadySchedulableNodesOrDie(fr.ClientSet)
		ips := e2enode.CollectAddresses(nodes, v1.NodeInternalIP)

		if len(nodes.Items) < 2 {
			framework.Skipf(
				"Test requires >= 2 Ready nodes, but there are only %v nodes",
				len(nodes.Items))
		}

		type NodeInfo struct {
			node   *v1.Node
			name   string
			nodeIP string
		}

		clientNodeInfo := NodeInfo{
			node:   &nodes.Items[0],
			name:   nodes.Items[0].Name,
			nodeIP: ips[0],
		}

		serverNodeInfo := NodeInfo{
			node:   &nodes.Items[1],
			name:   nodes.Items[1].Name,
			nodeIP: ips[1],
		}

		zero := int64(0)

		// Some distributions (Ubuntu 16.04 etc.) don't support the proc file.
		_, err := e2essh.IssueSSHCommandWithResult(
			"ls /proc/net/nf_conntrack",
			framework.TestContext.Provider,
			clientNodeInfo.node)
		if err != nil && strings.Contains(err.Error(), "No such file or directory") {
			framework.Skipf("The node %s does not support /proc/net/nf_conntrack", clientNodeInfo.name)
		}
		framework.ExpectNoError(err)

		clientPodSpec := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "e2e-net-client",
				Namespace: fr.Namespace.Name,
				Labels:    map[string]string{"app": "e2e-net-client"},
			},
			Spec: v1.PodSpec{
				NodeName: clientNodeInfo.name,
				Containers: []v1.Container{
					{
						Name:            "e2e-net-client",
						Image:           kubeProxyE2eImage,
						ImagePullPolicy: "Always",
						Args: []string{
							"net", "--serve", fmt.Sprintf("0.0.0.0:%d", testDaemonHTTPPort),
						},
					},
				},
				TerminationGracePeriodSeconds: &zero,
			},
		}

		serverPodSpec := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "e2e-net-server",
				Namespace: fr.Namespace.Name,
				Labels:    map[string]string{"app": "e2e-net-server"},
			},
			Spec: v1.PodSpec{
				NodeName: serverNodeInfo.name,
				Containers: []v1.Container{
					{
						Name:            "e2e-net-server",
						Image:           kubeProxyE2eImage,
						ImagePullPolicy: "Always",
						Args: []string{
							"net",
							"--runner", "nat-closewait-server",
							"--options",
							fmt.Sprintf(`{"LocalAddr":"0.0.0.0:%v", "PostFindTimeoutSeconds":%v}`,
								testDaemonTCPPort,
								postFinTimeoutSeconds),
						},
						Ports: []v1.ContainerPort{
							{
								Name:          "tcp",
								ContainerPort: testDaemonTCPPort,
								HostPort:      testDaemonTCPPort,
							},
						},
					},
				},
				TerminationGracePeriodSeconds: &zero,
			},
		}

		ginkgo.By(fmt.Sprintf(
			"Launching a server daemon on node %v (node ip: %v, image: %v)",
			serverNodeInfo.name,
			serverNodeInfo.nodeIP,
			kubeProxyE2eImage))
		fr.PodClient().CreateSync(serverPodSpec)

		ginkgo.By(fmt.Sprintf(
			"Launching a client daemon on node %v (node ip: %v, image: %v)",
			clientNodeInfo.name,
			clientNodeInfo.nodeIP,
			kubeProxyE2eImage))
		fr.PodClient().CreateSync(clientPodSpec)

		ginkgo.By("Make client connect")

		options := nat.CloseWaitClientOptions{
			RemoteAddr: fmt.Sprintf("%v:%v",
				serverNodeInfo.nodeIP, testDaemonTCPPort),
			TimeoutSeconds:        timeoutSeconds,
			PostFinTimeoutSeconds: 0,
			LeakConnection:        true,
		}

		jsonBytes, err := json.Marshal(options)
		cmd := fmt.Sprintf(
			`curl -X POST http://localhost:%v/run/nat-closewait-client -d `+
				`'%v' 2>/dev/null`,
			testDaemonHTTPPort,
			string(jsonBytes))
		framework.RunHostCmdOrDie(fr.Namespace.Name, "e2e-net-client", cmd)

		<-time.After(time.Duration(1) * time.Second)

		ginkgo.By("Checking /proc/net/nf_conntrack for the timeout")
		// If test flakes occur here, then this check should be performed
		// in a loop as there may be a race with the client connecting.
		e2essh.IssueSSHCommandWithResult(
			fmt.Sprintf("sudo cat /proc/net/nf_conntrack | grep 'dport=%v'",
				testDaemonTCPPort),
			framework.TestContext.Provider,
			clientNodeInfo.node)

		// Timeout in seconds is available as the fifth column from
		// /proc/net/nf_conntrack.
		result, err := e2essh.IssueSSHCommandWithResult(
			fmt.Sprintf(
				"sudo cat /proc/net/nf_conntrack "+
					"| grep 'CLOSE_WAIT.*dst=%v.*dport=%v' "+
					"| tail -n 1"+
					"| awk '{print $5}' ",
				serverNodeInfo.nodeIP,
				testDaemonTCPPort),
			framework.TestContext.Provider,
			clientNodeInfo.node)
		framework.ExpectNoError(err)

		timeoutSeconds, err := strconv.Atoi(strings.TrimSpace(result.Stdout))
		framework.ExpectNoError(err)

		// These must be synchronized from the default values set in
		// pkg/apis/../defaults.go ConntrackTCPCloseWaitTimeout. The
		// current defaults are hidden in the initialization code.
		const epsilonSeconds = 60
		const expectedTimeoutSeconds = 60 * 60

		e2elog.Logf("conntrack entry timeout was: %v, expected: %v",
			timeoutSeconds, expectedTimeoutSeconds)

		gomega.Expect(math.Abs(float64(timeoutSeconds - expectedTimeoutSeconds))).Should(
			gomega.BeNumerically("<", (epsilonSeconds)))
	})

	// Regression test for #74839, where:
	// Packets considered INVALID by conntrack are now dropped. In particular, this fixes
	// a problem where spurious retransmits in a long-running TCP connection to a service
	// IP could result in the connection being closed with the error "Connection reset by
	// peer"
	ginkgo.It("should resolve connrection reset issue #74839 [Slow]", func() {
		serverLabel := map[string]string{
			"app": "boom-server",
		}
		clientLabel := map[string]string{
			"app": "client",
		}

		serverPod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   "boom-server",
				Labels: serverLabel,
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "boom-server",
						Image: "gcr.io/kubernetes-e2e-test-images/regression-issue-74839-amd64:1.0",
						Ports: []v1.ContainerPort{
							{
								ContainerPort: 9000, // Default port exposed by boom-server
							},
						},
					},
				},
				Affinity: &v1.Affinity{
					PodAntiAffinity: &v1.PodAntiAffinity{
						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
							{
								LabelSelector: &metav1.LabelSelector{
									MatchLabels: clientLabel,
								},
								TopologyKey: "kubernetes.io/hostname",
							},
						},
					},
				},
			},
		}
		_, err := fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Create(serverPod)
		framework.ExpectNoError(err)

		ginkgo.By("Server pod created")

		svc := &v1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name: "boom-server",
			},
			Spec: v1.ServiceSpec{
				Selector: serverLabel,
				Ports: []v1.ServicePort{
					{
						Protocol: v1.ProtocolTCP,
						Port:     9000,
					},
				},
			},
		}
		_, err = fr.ClientSet.CoreV1().Services(fr.Namespace.Name).Create(svc)
		framework.ExpectNoError(err)

		ginkgo.By("Server service created")

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   "startup-script",
				Labels: clientLabel,
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "startup-script",
						Image: imageutils.GetE2EImage(imageutils.StartupScript),
						Command: []string{
							"bash", "-c", "while true; do sleep 2; nc boom-server 9000& done",
						},
					},
				},
				Affinity: &v1.Affinity{
					PodAntiAffinity: &v1.PodAntiAffinity{
						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
							{
								LabelSelector: &metav1.LabelSelector{
									MatchLabels: serverLabel,
								},
								TopologyKey: "kubernetes.io/hostname",
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}
		_, err = fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Create(pod)
		framework.ExpectNoError(err)

		ginkgo.By("Client pod created")

		for i := 0; i < 20; i++ {
			time.Sleep(3 * time.Second)
			resultPod, err := fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Get(serverPod.Name, metav1.GetOptions{})
			framework.ExpectNoError(err)
			gomega.Expect(resultPod.Status.ContainerStatuses[0].LastTerminationState.Terminated).Should(gomega.BeNil())
		}
	})
})
<commit_msg>Prevent resultPod.Status.ContainerStatuses from being empty.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package network

import (
	"encoding/json"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
	"k8s.io/kubernetes/test/images/agnhost/net/nat"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
)

var kubeProxyE2eImage = imageutils.GetE2EImage(imageutils.Agnhost)

var _ = SIGDescribe("Network", func() {
	const (
		testDaemonHTTPPort    = 11301
		testDaemonTCPPort     = 11302
		timeoutSeconds        = 10
		postFinTimeoutSeconds = 5
	)

	fr := framework.NewDefaultFramework("network")

	ginkgo.It("should set TCP CLOSE_WAIT timeout", func() {
		nodes := framework.GetReadySchedulableNodesOrDie(fr.ClientSet)
		ips := e2enode.CollectAddresses(nodes, v1.NodeInternalIP)

		if len(nodes.Items) < 2 {
			framework.Skipf(
				"Test requires >= 2 Ready nodes, but there are only %v nodes",
				len(nodes.Items))
		}

		type NodeInfo struct {
			node   *v1.Node
			name   string
			nodeIP string
		}

		clientNodeInfo := NodeInfo{
			node:   &nodes.Items[0],
			name:   nodes.Items[0].Name,
			nodeIP: ips[0],
		}

		serverNodeInfo := NodeInfo{
			node:   &nodes.Items[1],
			name:   nodes.Items[1].Name,
			nodeIP: ips[1],
		}

		zero := int64(0)

		// Some distributions (Ubuntu 16.04 etc.) don't support the proc file.
		_, err := e2essh.IssueSSHCommandWithResult(
			"ls /proc/net/nf_conntrack",
			framework.TestContext.Provider,
			clientNodeInfo.node)
		if err != nil && strings.Contains(err.Error(), "No such file or directory") {
			framework.Skipf("The node %s does not support /proc/net/nf_conntrack", clientNodeInfo.name)
		}
		framework.ExpectNoError(err)

		clientPodSpec := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "e2e-net-client",
				Namespace: fr.Namespace.Name,
				Labels:    map[string]string{"app": "e2e-net-client"},
			},
			Spec: v1.PodSpec{
				NodeName: clientNodeInfo.name,
				Containers: []v1.Container{
					{
						Name:            "e2e-net-client",
						Image:           kubeProxyE2eImage,
						ImagePullPolicy: "Always",
						Args: []string{
							"net", "--serve", fmt.Sprintf("0.0.0.0:%d", testDaemonHTTPPort),
						},
					},
				},
				TerminationGracePeriodSeconds: &zero,
			},
		}

		serverPodSpec := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "e2e-net-server",
				Namespace: fr.Namespace.Name,
				Labels:    map[string]string{"app": "e2e-net-server"},
			},
			Spec: v1.PodSpec{
				NodeName: serverNodeInfo.name,
				Containers: []v1.Container{
					{
						Name:            "e2e-net-server",
						Image:           kubeProxyE2eImage,
						ImagePullPolicy: "Always",
						Args: []string{
							"net",
							"--runner", "nat-closewait-server",
							"--options",
							fmt.Sprintf(`{"LocalAddr":"0.0.0.0:%v", "PostFindTimeoutSeconds":%v}`,
								testDaemonTCPPort,
								postFinTimeoutSeconds),
						},
						Ports: []v1.ContainerPort{
							{
								Name:          "tcp",
								ContainerPort: testDaemonTCPPort,
								HostPort:      testDaemonTCPPort,
							},
						},
					},
				},
				TerminationGracePeriodSeconds: &zero,
			},
		}

		ginkgo.By(fmt.Sprintf(
			"Launching a server daemon on node %v (node ip: %v, image: %v)",
			serverNodeInfo.name,
			serverNodeInfo.nodeIP,
			kubeProxyE2eImage))
		fr.PodClient().CreateSync(serverPodSpec)

		ginkgo.By(fmt.Sprintf(
			"Launching a client daemon on node %v (node ip: %v, image: %v)",
			clientNodeInfo.name,
			clientNodeInfo.nodeIP,
			kubeProxyE2eImage))
		fr.PodClient().CreateSync(clientPodSpec)

		ginkgo.By("Make client connect")

		options := nat.CloseWaitClientOptions{
			RemoteAddr: fmt.Sprintf("%v:%v",
				serverNodeInfo.nodeIP, testDaemonTCPPort),
			TimeoutSeconds:        timeoutSeconds,
			PostFinTimeoutSeconds: 0,
			LeakConnection:        true,
		}

		jsonBytes, err := json.Marshal(options)
		cmd := fmt.Sprintf(
			`curl -X POST http://localhost:%v/run/nat-closewait-client -d `+
				`'%v' 2>/dev/null`,
			testDaemonHTTPPort,
			string(jsonBytes))
		framework.RunHostCmdOrDie(fr.Namespace.Name, "e2e-net-client", cmd)

		<-time.After(time.Duration(1) * time.Second)

		ginkgo.By("Checking /proc/net/nf_conntrack for the timeout")
		// If test flakes occur here, then this check should be performed
		// in a loop as there may be a race with the client connecting.
		e2essh.IssueSSHCommandWithResult(
			fmt.Sprintf("sudo cat /proc/net/nf_conntrack | grep 'dport=%v'",
				testDaemonTCPPort),
			framework.TestContext.Provider,
			clientNodeInfo.node)

		// Timeout in seconds is available as the fifth column from
		// /proc/net/nf_conntrack.
		result, err := e2essh.IssueSSHCommandWithResult(
			fmt.Sprintf(
				"sudo cat /proc/net/nf_conntrack "+
					"| grep 'CLOSE_WAIT.*dst=%v.*dport=%v' "+
					"| tail -n 1"+
					"| awk '{print $5}' ",
				serverNodeInfo.nodeIP,
				testDaemonTCPPort),
			framework.TestContext.Provider,
			clientNodeInfo.node)
		framework.ExpectNoError(err)

		timeoutSeconds, err := strconv.Atoi(strings.TrimSpace(result.Stdout))
		framework.ExpectNoError(err)

		// These must be synchronized from the default values set in
		// pkg/apis/../defaults.go ConntrackTCPCloseWaitTimeout. The
		// current defaults are hidden in the initialization code.
		const epsilonSeconds = 60
		const expectedTimeoutSeconds = 60 * 60

		e2elog.Logf("conntrack entry timeout was: %v, expected: %v",
			timeoutSeconds, expectedTimeoutSeconds)

		gomega.Expect(math.Abs(float64(timeoutSeconds - expectedTimeoutSeconds))).Should(
			gomega.BeNumerically("<", (epsilonSeconds)))
	})

	// Regression test for #74839, where:
	// Packets considered INVALID by conntrack are now dropped. In particular, this fixes
	// a problem where spurious retransmits in a long-running TCP connection to a service
	// IP could result in the connection being closed with the error "Connection reset by
	// peer"
	ginkgo.It("should resolve connrection reset issue #74839 [Slow]", func() {
		serverLabel := map[string]string{
			"app": "boom-server",
		}
		clientLabel := map[string]string{
			"app": "client",
		}

		serverPod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   "boom-server",
				Labels: serverLabel,
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "boom-server",
						Image: "gcr.io/kubernetes-e2e-test-images/regression-issue-74839-amd64:1.0",
						Ports: []v1.ContainerPort{
							{
								ContainerPort: 9000, // Default port exposed by boom-server
							},
						},
					},
				},
				Affinity: &v1.Affinity{
					PodAntiAffinity: &v1.PodAntiAffinity{
						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
							{
								LabelSelector: &metav1.LabelSelector{
									MatchLabels: clientLabel,
								},
								TopologyKey: "kubernetes.io/hostname",
							},
						},
					},
				},
			},
		}
		_, err := fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Create(serverPod)
		framework.ExpectNoError(err)

		err = e2epod.WaitForPodsRunningReady(fr.ClientSet, fr.Namespace.Name, 1, 0, framework.PodReadyBeforeTimeout, map[string]string{})
		framework.ExpectNoError(err)

		ginkgo.By("Server pod created")

		svc := &v1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name: "boom-server",
			},
			Spec: v1.ServiceSpec{
				Selector: serverLabel,
				Ports: []v1.ServicePort{
					{
						Protocol: v1.ProtocolTCP,
						Port:     9000,
					},
				},
			},
		}
		_, err = fr.ClientSet.CoreV1().Services(fr.Namespace.Name).Create(svc)
		framework.ExpectNoError(err)

		ginkgo.By("Server service created")

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   "startup-script",
				Labels: clientLabel,
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "startup-script",
						Image: imageutils.GetE2EImage(imageutils.StartupScript),
						Command: []string{
							"bash", "-c", "while true; do sleep 2; nc boom-server 9000& done",
						},
					},
				},
				Affinity: &v1.Affinity{
					PodAntiAffinity: &v1.PodAntiAffinity{
						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
							{
								LabelSelector: &metav1.LabelSelector{
									MatchLabels: serverLabel,
								},
								TopologyKey: "kubernetes.io/hostname",
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}
		_, err = fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Create(pod)
		framework.ExpectNoError(err)

		ginkgo.By("Client pod created")

		for i := 0; i < 20; i++ {
			time.Sleep(3 * time.Second)
			resultPod, err := fr.ClientSet.CoreV1().Pods(fr.Namespace.Name).Get(serverPod.Name, metav1.GetOptions{})
			framework.ExpectNoError(err)
			gomega.Expect(resultPod.Status.ContainerStatuses[0].LastTerminationState.Terminated).Should(gomega.BeNil())
		}
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/golang/glog"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
)

var testTimeoutSeconds = flag.Duration("test-timeout", 45*time.Minute, "How long (in golang duration format) to wait for ginkgo tests to complete.")
var resultsDir = flag.String("results-dir", "/tmp/", "Directory to scp test results to.")

const archiveName = "e2e_node_test.tar.gz"

func CreateTestArchive(suite TestSuite, systemSpecName string) (string, error) {
	glog.V(2).Infof("Building archive...")
	tardir, err := ioutil.TempDir("", "node-e2e-archive")
	if err != nil {
		return "", fmt.Errorf("failed to create temporary directory %v.", err)
	}
	defer os.RemoveAll(tardir)

	// Call the suite function to setup the test package.
	err = suite.SetupTestPackage(tardir, systemSpecName)
	if err != nil {
		return "", fmt.Errorf("failed to setup test package %q: %v", tardir, err)
	}

	// Build the tar
	out, err := exec.Command("tar", "-zcvf", archiveName, "-C", tardir, ".").CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("failed to build tar %v.  Output:\n%s", err, out)
	}

	dir, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("failed to get working directory %v.", err)
	}
	return filepath.Join(dir, archiveName), nil
}

// Returns the command output, whether the exit was ok, and any errors
// TODO(random-liu): junitFilePrefix is not prefix actually, the file name is junit-junitFilePrefix.xml. Change the variable name.
func RunRemote(suite TestSuite, archive string, host string, cleanup bool, imageDesc, junitFilePrefix string, testArgs string, ginkgoArgs string, systemSpecName string) (string, bool, error) {
	// Create the temp staging directory
	glog.V(2).Infof("Staging test binaries on %q", host)
	workspace := fmt.Sprintf("/tmp/node-e2e-%s", getTimestamp())
	// Do not sudo here, so that we can use scp to copy test archive to the directdory.
	if output, err := SSHNoSudo(host, "mkdir", workspace); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to create workspace directory %q on host %q: %v output: %q", workspace, host, err, output)
	}
	if cleanup {
		defer func() {
			output, err := SSH(host, "rm", "-rf", workspace)
			if err != nil {
				glog.Errorf("failed to cleanup workspace %q on host %q: %v.  Output:\n%s", workspace, host, err, output)
			}
		}()
	}

	// Copy the archive to the staging directory
	if output, err := runSSHCommand("scp", archive, fmt.Sprintf("%s:%s/", GetHostnameOrIp(host), workspace)); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to copy test archive: %v, output: %q", err, output)
	}

	// Extract the archive
	cmd := getSSHCommand(" && ",
		fmt.Sprintf("cd %s", workspace),
		fmt.Sprintf("tar -xzvf ./%s", archiveName),
	)
	glog.V(2).Infof("Extracting tar on %q", host)
	// Do not use sudo here, because `sudo tar -x` will recover the file ownership inside the tar ball, but
	// we want the extracted files to be owned by the current user.
	if output, err := SSHNoSudo(host, "sh", "-c", cmd); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to extract test archive: %v, output: %q", err, output)
	}

	// Create the test result directory.
	resultDir := filepath.Join(workspace, "results")
	if output, err := SSHNoSudo(host, "mkdir", resultDir); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to create test result directory %q on host %q: %v output: %q", resultDir, host, err, output)
	}

	glog.V(2).Infof("Running test on %q", host)
	output, err := suite.RunTest(host, workspace, resultDir, imageDesc, junitFilePrefix, testArgs, ginkgoArgs, systemSpecName, *testTimeoutSeconds)

	aggErrs := []error{}
	// Do not log the output here, let the caller deal with the test output.
	if err != nil {
		aggErrs = append(aggErrs, err)
		collectSystemLog(host, workspace)
	}

	glog.V(2).Infof("Copying test artifacts from %q", host)
	scpErr := getTestArtifacts(host, workspace)
	if scpErr != nil {
		aggErrs = append(aggErrs, scpErr)
	}

	return output, len(aggErrs) == 0, utilerrors.NewAggregate(aggErrs)
}

// timestampFormat is the timestamp format used in the node e2e directory name.
const timestampFormat = "20060102T150405"

func getTimestamp() string {
	return fmt.Sprintf(time.Now().Format(timestampFormat))
}

func getTestArtifacts(host, testDir string) error {
	logPath := filepath.Join(*resultsDir, host)
	if err := os.MkdirAll(logPath, 0755); err != nil {
		return fmt.Errorf("failed to create log directory %q: %v", logPath, err)
	}
	// Copy logs to artifacts/hostname
	_, err := runSSHCommand("scp", "-r", fmt.Sprintf("%s:%s/results/*.log", GetHostnameOrIp(host), testDir), logPath)
	if err != nil {
		return err
	}
	// Copy json files (if any) to artifacts.
	if _, err = SSH(host, "ls", fmt.Sprintf("%s/results/*.json", testDir)); err == nil {
		_, err = runSSHCommand("scp", "-r", fmt.Sprintf("%s:%s/results/*.json", GetHostnameOrIp(host), testDir), *resultsDir)
		if err != nil {
			return err
		}
	}
	// Copy junit to the top of artifacts
	_, err = runSSHCommand("scp", fmt.Sprintf("%s:%s/results/junit*", GetHostnameOrIp(host), testDir), *resultsDir)
	if err != nil {
		return err
	}
	return nil
}

// collectSystemLog is a temporary hack to collect system log when encountered on
// unexpected error.
func collectSystemLog(host, workspace string) {
	// Encountered an unexpected error. The remote test harness may not
	// have finished retrieved and stored all the logs in this case. Try
	// to get some logs for debugging purposes.
	// TODO: This is a best-effort, temporary hack that only works for
	// journald nodes. We should have a more robust way to collect logs.
	var (
		logName  = "system.log"
		logPath  = fmt.Sprintf("/tmp/%s-%s", getTimestamp(), logName)
		destPath = fmt.Sprintf("%s/%s-%s", *resultsDir, host, logName)
	)
	glog.V(2).Infof("Test failed unexpectedly. Attempting to retreiving system logs (only works for nodes with journald)")
	// Try getting the system logs from journald and store it to a file.
	// Don't reuse the original test directory on the remote host because
	// it could've be been removed if the node was rebooted.
	if output, err := SSH(host, "sh", "-c", fmt.Sprintf("'journalctl --system --all > %s'", logPath)); err == nil {
		glog.V(2).Infof("Got the system logs from journald; copying it back...")
		if output, err := runSSHCommand("scp", fmt.Sprintf("%s:%s", GetHostnameOrIp(host), logPath), destPath); err != nil {
			glog.V(2).Infof("Failed to copy the log: err: %v, output: %q", err, output)
		}
	} else {
		glog.V(2).Infof("Failed to run journactl (normal if it doesn't exist on the node): %v, output: %q", err, output)
	}
}

// WriteLog is a temporary function to make it possible to write log
// in the runner. This is used to collect serial console log.
// TODO(random-liu): Use the log-dump script in cluster e2e.
func WriteLog(host, filename, content string) error {
	logPath := filepath.Join(*resultsDir, host)
	if err := os.MkdirAll(logPath, 0755); err != nil {
		return fmt.Errorf("failed to create log directory %q: %v", logPath, err)
	}
	f, err := os.Create(filepath.Join(logPath, filename))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(content)
	return err
}
<commit_msg>remove redundant param in e2e_node/remote<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/golang/glog"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
)

var testTimeoutSeconds = flag.Duration("test-timeout", 45*time.Minute, "How long (in golang duration format) to wait for ginkgo tests to complete.")
var resultsDir = flag.String("results-dir", "/tmp/", "Directory to scp test results to.")

const archiveName = "e2e_node_test.tar.gz"

func CreateTestArchive(suite TestSuite, systemSpecName string) (string, error) {
	glog.V(2).Infof("Building archive...")
	tardir, err := ioutil.TempDir("", "node-e2e-archive")
	if err != nil {
		return "", fmt.Errorf("failed to create temporary directory %v.", err)
	}
	defer os.RemoveAll(tardir)

	// Call the suite function to setup the test package.
	err = suite.SetupTestPackage(tardir, systemSpecName)
	if err != nil {
		return "", fmt.Errorf("failed to setup test package %q: %v", tardir, err)
	}

	// Build the tar
	out, err := exec.Command("tar", "-zcvf", archiveName, "-C", tardir, ".").CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("failed to build tar %v.  Output:\n%s", err, out)
	}

	dir, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("failed to get working directory %v.", err)
	}
	return filepath.Join(dir, archiveName), nil
}

// Returns the command output, whether the exit was ok, and any errors
// TODO(random-liu): junitFilePrefix is not prefix actually, the file name is junit-junitFilePrefix.xml. Change the variable name.
func RunRemote(suite TestSuite, archive string, host string, cleanup bool, imageDesc, junitFilePrefix string, testArgs string, ginkgoArgs string, systemSpecName string) (string, bool, error) {
	// Create the temp staging directory
	glog.V(2).Infof("Staging test binaries on %q", host)
	workspace := fmt.Sprintf("/tmp/node-e2e-%s", getTimestamp())
	// Do not sudo here, so that we can use scp to copy test archive to the directdory.
	if output, err := SSHNoSudo(host, "mkdir", workspace); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to create workspace directory %q on host %q: %v output: %q", workspace, host, err, output)
	}
	if cleanup {
		defer func() {
			output, err := SSH(host, "rm", "-rf", workspace)
			if err != nil {
				glog.Errorf("failed to cleanup workspace %q on host %q: %v.  Output:\n%s", workspace, host, err, output)
			}
		}()
	}

	// Copy the archive to the staging directory
	if output, err := runSSHCommand("scp", archive, fmt.Sprintf("%s:%s/", GetHostnameOrIp(host), workspace)); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to copy test archive: %v, output: %q", err, output)
	}

	// Extract the archive
	cmd := getSSHCommand(" && ",
		fmt.Sprintf("cd %s", workspace),
		fmt.Sprintf("tar -xzvf ./%s", archiveName),
	)
	glog.V(2).Infof("Extracting tar on %q", host)
	// Do not use sudo here, because `sudo tar -x` will recover the file ownership inside the tar ball, but
	// we want the extracted files to be owned by the current user.
	if output, err := SSHNoSudo(host, "sh", "-c", cmd); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to extract test archive: %v, output: %q", err, output)
	}

	// Create the test result directory.
	resultDir := filepath.Join(workspace, "results")
	if output, err := SSHNoSudo(host, "mkdir", resultDir); err != nil {
		// Exit failure with the error
		return "", false, fmt.Errorf("failed to create test result directory %q on host %q: %v output: %q", resultDir, host, err, output)
	}

	glog.V(2).Infof("Running test on %q", host)
	output, err := suite.RunTest(host, workspace, resultDir, imageDesc, junitFilePrefix, testArgs, ginkgoArgs, systemSpecName, *testTimeoutSeconds)

	aggErrs := []error{}
	// Do not log the output here, let the caller deal with the test output.
	if err != nil {
		aggErrs = append(aggErrs, err)
		collectSystemLog(host)
	}

	glog.V(2).Infof("Copying test artifacts from %q", host)
	scpErr := getTestArtifacts(host, workspace)
	if scpErr != nil {
		aggErrs = append(aggErrs, scpErr)
	}

	return output, len(aggErrs) == 0, utilerrors.NewAggregate(aggErrs)
}

// timestampFormat is the timestamp format used in the node e2e directory name.
const timestampFormat = "20060102T150405"

func getTimestamp() string {
	return fmt.Sprintf(time.Now().Format(timestampFormat))
}

func getTestArtifacts(host, testDir string) error {
	logPath := filepath.Join(*resultsDir, host)
	if err := os.MkdirAll(logPath, 0755); err != nil {
		return fmt.Errorf("failed to create log directory %q: %v", logPath, err)
	}
	// Copy logs to artifacts/hostname
	_, err := runSSHCommand("scp", "-r", fmt.Sprintf("%s:%s/results/*.log", GetHostnameOrIp(host), testDir), logPath)
	if err != nil {
		return err
	}
	// Copy json files (if any) to artifacts.
	if _, err = SSH(host, "ls", fmt.Sprintf("%s/results/*.json", testDir)); err == nil {
		_, err = runSSHCommand("scp", "-r", fmt.Sprintf("%s:%s/results/*.json", GetHostnameOrIp(host), testDir), *resultsDir)
		if err != nil {
			return err
		}
	}
	// Copy junit to the top of artifacts
	_, err = runSSHCommand("scp", fmt.Sprintf("%s:%s/results/junit*", GetHostnameOrIp(host), testDir), *resultsDir)
	if err != nil {
		return err
	}
	return nil
}

// collectSystemLog is a temporary hack to collect system log when encountered on
// unexpected error.
func collectSystemLog(host string) {
	// Encountered an unexpected error. The remote test harness may not
	// have finished retrieved and stored all the logs in this case. Try
	// to get some logs for debugging purposes.
	// TODO: This is a best-effort, temporary hack that only works for
	// journald nodes. We should have a more robust way to collect logs.
	var (
		logName  = "system.log"
		logPath  = fmt.Sprintf("/tmp/%s-%s", getTimestamp(), logName)
		destPath = fmt.Sprintf("%s/%s-%s", *resultsDir, host, logName)
	)
	glog.V(2).Infof("Test failed unexpectedly. Attempting to retrieving system logs (only works for nodes with journald)")
	// Try getting the system logs from journald and store it to a file.
	// Don't reuse the original test directory on the remote host because
	// it could've be been removed if the node was rebooted.
	if output, err := SSH(host, "sh", "-c", fmt.Sprintf("'journalctl --system --all > %s'", logPath)); err == nil {
		glog.V(2).Infof("Got the system logs from journald; copying it back...")
		if output, err := runSSHCommand("scp", fmt.Sprintf("%s:%s", GetHostnameOrIp(host), logPath), destPath); err != nil {
			glog.V(2).Infof("Failed to copy the log: err: %v, output: %q", err, output)
		}
	} else {
		glog.V(2).Infof("Failed to run journactl (normal if it doesn't exist on the node): %v, output: %q", err, output)
	}
}

// WriteLog is a temporary function to make it possible to write log
// in the runner. This is used to collect serial console log.
// TODO(random-liu): Use the log-dump script in cluster e2e.
func WriteLog(host, filename, content string) error {
	logPath := filepath.Join(*resultsDir, host)
	if err := os.MkdirAll(logPath, 0755); err != nil {
		return fmt.Errorf("failed to create log directory %q: %v", logPath, err)
	}
	f, err := os.Create(filepath.Join(logPath, filename))
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(content)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package stack

import (
	"bytes"
	"fmt"
	"go/ast"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/maruel/ut"
)

func overrideEnv(env []string, key, value string) []string {
	prefix := key + "="
	for i, e := range env {
		if strings.HasPrefix(e, prefix) {
			env[i] = value
			return env
		}
	}
	return append(env, prefix+value)
}

func getCrash(t *testing.T, content string) (string, []byte) {
	name, err := ioutil.TempDir("", "panicparse")
	ut.AssertEqual(t, nil, err)
	defer os.RemoveAll(name)
	main := filepath.Join(name, "main.go")
	ut.AssertEqual(t, nil, ioutil.WriteFile(main, []byte(content), 0500))
	cmd := exec.Command("go", "run", main)
	// Use the Go 1.4 compatible format.
	cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "2")
	out, _ := cmd.CombinedOutput()
	return main, out
}

func TestAugment(t *testing.T) {
	extra := &bytes.Buffer{}
	main, content := getCrash(t, mainSource)
	goroutines, err := ParseDump(bytes.NewBuffer(content), extra)
	ut.AssertEqual(t, nil, err)
	// On go1.4, there's one less space.
	actual := extra.String()
	if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
		t.Fatalf("Unexpected panic output:\n%#v", actual)
	}
	// The number of goroutine alive depends on the runtime environment. It
	// doesn't matter as only the crashing thread is of importance.
	ut.AssertEqual(t, true, len(goroutines) >= 1)

	// Preload content so no disk I/O is done.
	c := &cache{files: map[string][]byte{main: []byte(mainSource)}}
	c.augmentGoroutine(&goroutines[0])
	pointer := uint64(0xfffffffff)
	pointerStr := fmt.Sprintf("0x%x", pointer)
	expected := Stack{
		Calls: []Call{
			{
				SourcePath: filepath.Join(goroot, "src", "runtime", "panic.go"),
				Func:       Function{"panic"},
				Args:       Args{Values: []Arg{{Value: pointer}, {Value: pointer}}},
			},
			{
				Func: Function{"main.S.f1"},
			},
			{
				Func: Function{"main.(*S).f2"},
				Args: Args{
					Values:    []Arg{{Value: pointer}},
					Processed: []string{"*S(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f3"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}, {Value: 1}},
					Processed: []string{"string(" + pointerStr + ", len=3)", "1"},
				},
			},
			{
				Func: Function{"main.f4"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}},
					Processed: []string{"string(" + pointerStr + ", len=3)"},
				},
			},
			{
				Func: Function{"main.f5"},
				Args: Args{
					Values:    []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
					Processed: []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "interface{}(0x0)"},
					Elided:    true,
				},
			},
			{
				Func: Function{"main.f6"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: pointer}},
					Processed: []string{"error(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f7"},
				Args: Args{
					Values:    []Arg{{}, {}},
					Processed: []string{"error(0x0)"},
				},
			},
			{
				Func: Function{"main.f8"},
				Args: Args{
					Values:    []Arg{{Value: 0x3fe0000000000000}, {Value: 0xc440066666}},
					Processed: []string{"0.5", "2.1"},
				},
			},
			{
				Func: Function{"main.f9"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]int(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f10"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]interface{}(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f11"},
				Args: Args{
					Values:    []Arg{{}},
					Processed: []string{"func(0x0)"},
				},
			},
			{
				Func: Function{"main.f12"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}, {Value: 2}},
					Processed: []string{"func(" + pointerStr + ")", "func(0x2)"},
				},
			},
			{
				Func: Function{"main.f13"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}},
					Processed: []string{"string(" + pointerStr + ", len=2)"},
				},
			},
			{
				Func: Function{"main.main"},
			},
		},
	}
	s := goroutines[0].Signature.Stack
	// On Travis, runtime.GOROOT() != what is dumped when running a command via
	// "go run". E.g. GOROOT() were "/usr/local/go" yet the path output via a
	// subcommand is "/home/travis/.gimme/versions/go1.4.linux.amd64". Kidding
	// me, right?
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].SourcePath, "panic.go"))
	s.Calls[0].SourcePath = expected.Calls[0].SourcePath
	// Zap out the panic() call, since its signature changed between go1.4 and
	// go1.5, it used to be runtime.gopanic().
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].Func.Raw, "panic"))
	s.Calls[0].Func = expected.Calls[0].Func

	// Zap out pointers.
	for i := range s.Calls {
		if i >= len(expected.Calls) {
			// When using GOTRACEBACK=2, it'll include runtime.main() and
			// runtime.goexit(). Ignore these since they could be changed in a future
			// version.
			s.Calls = s.Calls[:len(expected.Calls)]
			break
		}
		if i > 0 {
			ut.AssertEqual(t, true, s.Calls[i].Line > s.Calls[i-1].Line)
		}
		s.Calls[i].Line = 0
		for j := range s.Calls[i].Args.Values {
			if j >= len(expected.Calls[i].Args.Values) {
				break
			}
			if expected.Calls[i].Args.Values[j].Value == pointer {
				// Replace the pointer value.
				ut.AssertEqual(t, false, s.Calls[i].Args.Values[j].Value == 0)
				old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
				s.Calls[i].Args.Values[j].Value = pointer
				for k := range s.Calls[i].Args.Processed {
					s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
				}
			}
		}
		if expected.Calls[i].SourcePath == "" {
			expected.Calls[i].SourcePath = main
		}
	}
	// Zap out panic() exact line number.
	s.Calls[0].Line = 0
	ut.AssertEqual(t, expected, s)
}

func TestAugmentDummy(t *testing.T) {
	goroutines := []Goroutine{
		{
			Signature: Signature{
				Stack: Stack{
					Calls: []Call{{SourcePath: "missing.go"}},
				},
			},
		},
	}
	Augment(goroutines)
}

func TestLoad(t *testing.T) {
	c := &cache{
		files:  map[string][]byte{"bad.go": []byte("bad content")},
		parsed: map[string]*parsedFile{},
	}
	c.load("foo.asm")
	c.load("bad.go")
	c.load("doesnt_exist.go")
	ut.AssertEqual(t, 3, len(c.parsed))
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["foo.asm"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["bad.go"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["doesnt_exist.go"])
	ut.AssertEqual(t, (*ast.FuncDecl)(nil), c.getFuncAST(&Call{SourcePath: "other"}))
}

const mainSource = `// Exercises most code paths in processCall().

package main

import "errors"

type S struct {
}

func (s S) f1() {
	panic("ooh")
}

func (s *S) f2() {
	s.f1()
}

func f3(s string, i int) {
	(&S{}).f2()
}

func f4(s string) {
	f3(s, 1)
}

func f5(s1, s2, s3, s4, s5, s6, s7, s8, s9 int, s10 interface{}) {
	f4("ooh")
}

func f6(err error) {
	f5(0, 0, 0, 0, 0, 0, 0, 0, 0, nil)
}

func f7(error) {
	f6(errors.New("Ooh"))
}

func f8(a float64, b float32) {
	f7(nil)
}

func f9(a []int) {
	f8(0.5, 2.1)
}

func f10(a []interface{}) {
	f9(make([]int, 5, 7))
}

func f11(a func()) {
	f10(make([]interface{}, 5, 7))
}

func f12(a ...func()) {
	f11(nil)
}

func f13(s string) {
	// This asserts that a local function definition is not picked up by accident.
	a := func(i int) int {
		return 1 + i
	}
	_ = a(3)
	f12(nil, nil)
}

func main() {
	f13("yo")
}
`
<commit_msg>Update stack/source_test.go expectation with new float encoding on go 1.8<commit_after>// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package stack

import (
	"bytes"
	"fmt"
	"go/ast"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/maruel/ut"
)

func overrideEnv(env []string, key, value string) []string {
	prefix := key + "="
	for i, e := range env {
		if strings.HasPrefix(e, prefix) {
			env[i] = value
			return env
		}
	}
	return append(env, prefix+value)
}

func getCrash(t *testing.T, content string) (string, []byte) {
	name, err := ioutil.TempDir("", "panicparse")
	ut.AssertEqual(t, nil, err)
	defer os.RemoveAll(name)
	main := filepath.Join(name, "main.go")
	ut.AssertEqual(t, nil, ioutil.WriteFile(main, []byte(content), 0500))
	cmd := exec.Command("go", "run", main)
	// Use the Go 1.4 compatible format.
	cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "2")
	out, _ := cmd.CombinedOutput()
	return main, out
}

func TestAugment(t *testing.T) {
	extra := &bytes.Buffer{}
	main, content := getCrash(t, mainSource)
	goroutines, err := ParseDump(bytes.NewBuffer(content), extra)
	ut.AssertEqual(t, nil, err)
	// On go1.4, there's one less space.
	actual := extra.String()
	if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
		t.Fatalf("Unexpected panic output:\n%#v", actual)
	}
	// The number of goroutine alive depends on the runtime environment. It
	// doesn't matter as only the crashing thread is of importance.
	ut.AssertEqual(t, true, len(goroutines) >= 1)

	// Preload content so no disk I/O is done.
	c := &cache{files: map[string][]byte{main: []byte(mainSource)}}
	c.augmentGoroutine(&goroutines[0])
	pointer := uint64(0xfffffffff)
	pointerStr := fmt.Sprintf("0x%x", pointer)
	expected := Stack{
		Calls: []Call{
			{
				SourcePath: filepath.Join(goroot, "src", "runtime", "panic.go"),
				Func:       Function{"panic"},
				Args:       Args{Values: []Arg{{Value: pointer}, {Value: pointer}}},
			},
			{
				Func: Function{"main.S.f1"},
			},
			{
				Func: Function{"main.(*S).f2"},
				Args: Args{
					Values:    []Arg{{Value: pointer}},
					Processed: []string{"*S(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f3"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}, {Value: 1}},
					Processed: []string{"string(" + pointerStr + ", len=3)", "1"},
				},
			},
			{
				Func: Function{"main.f4"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 3}},
					Processed: []string{"string(" + pointerStr + ", len=3)"},
				},
			},
			{
				Func: Function{"main.f5"},
				Args: Args{
					Values:    []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
					Processed: []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "interface{}(0x0)"},
					Elided:    true,
				},
			},
			{
				Func: Function{"main.f6"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: pointer}},
					Processed: []string{"error(" + pointerStr + ")"},
				},
			},
			{
				Func: Function{"main.f7"},
				Args: Args{
					Values:    []Arg{{}, {}},
					Processed: []string{"error(0x0)"},
				},
			},
			{
				Func: Function{"main.f8"},
				Args: Args{
					Values:    []Arg{{Value: 0x3fe0000000000000}, {Value: 0xc440066666}},
					Processed: []string{"0.5", "2.1"},
				},
			},
			{
				Func: Function{"main.f9"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]int(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f10"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
					Processed: []string{"[]interface{}(" + pointerStr + " len=5 cap=7)"},
				},
			},
			{
				Func: Function{"main.f11"},
				Args: Args{
					Values:    []Arg{{}},
					Processed: []string{"func(0x0)"},
				},
			},
			{
				Func: Function{"main.f12"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}, {Value: 2}},
					Processed: []string{"func(" + pointerStr + ")", "func(0x2)"},
				},
			},
			{
				Func: Function{"main.f13"},
				Args: Args{
					Values:    []Arg{{Value: pointer}, {Value: 2}},
					Processed: []string{"string(" + pointerStr + ", len=2)"},
				},
			},
			{
				Func: Function{"main.main"},
			},
		},
	}
	s := goroutines[0].Signature.Stack
	// On Travis, runtime.GOROOT() != what is dumped when running a command via
	// "go run". E.g. GOROOT() were "/usr/local/go" yet the path output via a
	// subcommand is "/home/travis/.gimme/versions/go1.4.linux.amd64". Kidding
	// me, right?
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].SourcePath, "panic.go"))
	s.Calls[0].SourcePath = expected.Calls[0].SourcePath
	// Zap out the panic() call, since its signature changed between go1.4 and
	// go1.5, it used to be runtime.gopanic().
	ut.AssertEqual(t, true, strings.HasSuffix(s.Calls[0].Func.Raw, "panic"))
	s.Calls[0].Func = expected.Calls[0].Func

	// Zap out pointers.
	for i := range s.Calls {
		if i >= len(expected.Calls) {
			// When using GOTRACEBACK=2, it'll include runtime.main() and
			// runtime.goexit(). Ignore these since they could be changed in a future
			// version.
			s.Calls = s.Calls[:len(expected.Calls)]
			break
		}
		if i > 0 {
			ut.AssertEqual(t, true, s.Calls[i].Line > s.Calls[i-1].Line)
		}
		s.Calls[i].Line = 0
		for j := range s.Calls[i].Args.Values {
			if j >= len(expected.Calls[i].Args.Values) {
				break
			}
			if expected.Calls[i].Args.Values[j].Value == pointer {
				// Replace the pointer value.
				ut.AssertEqual(t, false, s.Calls[i].Args.Values[j].Value == 0)
				old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
				s.Calls[i].Args.Values[j].Value = pointer
				for k := range s.Calls[i].Args.Processed {
					s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
				}
			}
		}
		if expected.Calls[i].SourcePath == "" {
			expected.Calls[i].SourcePath = main
		}
	}
	// Zap out panic() exact line number.
	s.Calls[0].Line = 0

	// Zap out floating point, this is not deterministic. Verify the line # is
	// actually the right one.
	line := 8 // main.f8
	ut.AssertEqual(t, uint64(0xc440066666), expected.Calls[line].Args.Values[1].Value)
	if s.Calls[line].Args.Values[1].Value != expected.Calls[line].Args.Values[1].Value {
		// Try an alternate encoding of "2.1".
		expected.Calls[line].Args.Values[1].Value = 0x40066666
	}
	ut.AssertEqual(t, expected, s)
}

func TestAugmentDummy(t *testing.T) {
	goroutines := []Goroutine{
		{
			Signature: Signature{
				Stack: Stack{
					Calls: []Call{{SourcePath: "missing.go"}},
				},
			},
		},
	}
	Augment(goroutines)
}

func TestLoad(t *testing.T) {
	c := &cache{
		files:  map[string][]byte{"bad.go": []byte("bad content")},
		parsed: map[string]*parsedFile{},
	}
	c.load("foo.asm")
	c.load("bad.go")
	c.load("doesnt_exist.go")
	ut.AssertEqual(t, 3, len(c.parsed))
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["foo.asm"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["bad.go"])
	ut.AssertEqual(t, (*parsedFile)(nil), c.parsed["doesnt_exist.go"])
	ut.AssertEqual(t, (*ast.FuncDecl)(nil), c.getFuncAST(&Call{SourcePath: "other"}))
}

const mainSource = `// Exercises most code paths in processCall().

package main

import "errors"

type S struct {
}

func (s S) f1() {
	panic("ooh")
}

func (s *S) f2() {
	s.f1()
}

func f3(s string, i int) {
	(&S{}).f2()
}

func f4(s string) {
	f3(s, 1)
}

func f5(s1, s2, s3, s4, s5, s6, s7, s8, s9 int, s10 interface{}) {
	f4("ooh")
}

func f6(err error) {
	f5(0, 0, 0, 0, 0, 0, 0, 0, 0, nil)
}

func f7(error) {
	f6(errors.New("Ooh"))
}

func f8(a float64, b float32) {
	f7(nil)
}

func f9(a []int) {
	f8(0.5, 2.1)
}

func f10(a []interface{}) {
	f9(make([]int, 5, 7))
}

func f11(a func()) {
	f10(make([]interface{}, 5, 7))
}

func f12(a ...func()) {
	f11(nil)
}

func f13(s string) {
	// This asserts that a local function definition is not picked up by accident.
	a := func(i int) int {
		return 1 + i
	}
	_ = a(3)
	f12(nil, nil)
}

func main() {
	f13("yo")
}
`
<|endoftext|>