<commit_before>package kafka

import (
	. "testing"

	exporter "github.com/kawamuray/prometheus-kafka-consumer-group-exporter"
)

func TestParsingPartitionTableForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Note: This will only show information about consumers that use the Java consumer API (non-ZooKeeper-based consumers).\n",
		Stdout: `TOPIC                          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG        CONSUMER-ID                                       HOST                           CLIENT-ID
xxx           4          21555284        22970821        1415537    -                                                 -                              -
yyy           1          496377          525680          29303      -                                                 -                              -
yyy           7          478173          507408          29235      -                                                 -                              -`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "xxx",
			PartitionID:     "4",
			CurrentOffset:   21555284,
			Lag:             1415537,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
		{
			Topic:           "yyy",
			PartitionID:     "1",
			CurrentOffset:   496377,
			Lag:             29303,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
		{
			Topic:           "yyy",
			PartitionID:     "7",
			CurrentOffset:   478173,
			Lag:             29235,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
	}

	comparePartitionTable(t, kafka0_10_2_1DescribeGroupParser, output, expected)
	comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
}

func TestParsingNoActiveMemberErrorForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Consumer group '$groupId' has no active members.\n",
	}
	assertErrorParsing(t, kafka0_10_2_1DescribeGroupParser, output)
	assertErrorParsing(t, DefaultDescribeGroupParser(), output)
}

func TestParsingWhenRebalancingErrorForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Warning: Consumer group '$groupId' is rebalancing.\n",
	}
	assertErrorParsing(t, kafka0_10_2_1DescribeGroupParser, output)
	assertErrorParsing(t, DefaultDescribeGroupParser(), output)
}

func assertErrorParsing(t *T, parser DescribeGroupParser, output CommandOutput) {
	_, err := parser.Parse(output)
	if err == nil {
		t.Error("Expected an error.")
	}
}

func TestParsingPartitionTableForKafkaVersion0_10_0_1(t *T) {
	output := CommandOutput{
		Stdout: `GROUP                          TOPIC                          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             OWNER
foobar-consumer topic-A                      2          12345200        12345200        0               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.1
foobar-consumer topic-A                      1          45678335        45678337        2               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.2
foobar-consumer topic-A                      0          91011178        91011179        1               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.3`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "topic-A",
			PartitionID:     "2",
			CurrentOffset:   12345200,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.1",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "1",
			CurrentOffset:   45678335,
			Lag:             2,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.2",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "0",
			CurrentOffset:   91011178,
			Lag:             1,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.3",
		},
	}

	comparePartitionTable(t, kafka0_10_0_1DescribeGroupParser, output, expected)
	comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
}

func TestParsingPartitionTableForKafkaVersion0_9_0_1(t *T) {
	output := CommandOutput{
		Stdout: `GROUP, TOPIC, PARTITION, CURRENT OFFSET, LOG END OFFSET, LAG, OWNER
foobar-consumer, topic-A, 2, 12344967, 12344973, 6, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.1
foobar-consumer, topic-A, 1, 45678117, 45678117, 0, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.2
foobar-consumer, topic-A, 0, 91011145, 91011145, 0, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.3`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "topic-A",
			PartitionID:     "2",
			CurrentOffset:   12344967,
			Lag:             6,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.1",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "1",
			CurrentOffset:   45678117,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.2",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "0",
			CurrentOffset:   91011145,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.3",
		},
	}

	comparePartitionTable(t, kafka0_9_0_1DescribeGroupParser, output, expected)
	comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
}

func comparePartitionTable(t *T, parser DescribeGroupParser, output CommandOutput, expected []exporter.PartitionInfo) {
	values, err := parser.Parse(output)
	if err != nil {
		t.Fatal("Failed parsing. Parser:", parser, "Error:", err)
	}

	if len(values) != len(expected) {
		t.Fatal("Not same lengths. Was:", len(values), "Was:", len(expected))
	}
	for i, value := range values {
		comparePartitionInfo(t, parser, value, expected[i])
	}
}

func comparePartitionInfo(t *T, parser DescribeGroupParser, value, expected exporter.PartitionInfo) {
	if value, expected := value.Topic, expected.Topic; expected != value {
		t.Error("Wrong topic. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.PartitionID, expected.PartitionID; expected != value {
		t.Error("Wrong PartitionID. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.CurrentOffset, expected.CurrentOffset; expected != value {
		t.Error("Wrong CurrentOffset. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.Lag, expected.Lag; expected != value {
		t.Error("Wrong Lag. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.ClientID, expected.ClientID; expected != value {
		t.Error("Wrong ClientID. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.ConsumerAddress, expected.ConsumerAddress; expected != value {
		t.Error("Wrong ConsumerAddress. Parser:", parser, "Expected:", expected, "Was:", value)
	}
}

// Test Kafka 10.0.0 script unable to connect to describe group.
func TestKafka10_0_0ScriptDescribeGroupErrorOutput(t *T) {
	cannotConnectOutput := `Error while executing consumer group command Request GROUP_COORDINATOR failed on brokers List(localhost:9042 (id: -1 rack: null))
java.lang.RuntimeException: Request GROUP_COORDINATOR failed on brokers List(localhost:9042 (id: -1 rack: null))
	at kafka.admin.AdminClient.sendAnyNode(AdminClient.scala:67)
	at kafka.admin.AdminClient.findCoordinator(AdminClient.scala:72)
	at kafka.admin.AdminClient.describeGroup(AdminClient.scala:125)
	at kafka.admin.AdminClient.describeConsumerGroup(AdminClient.scala:147)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.describeGroup(ConsumerGroupCommand.scala:315)
	at kafka.admin.ConsumerGroupCommand$ConsumerGroupService$class.describe(ConsumerGroupCommand.scala:86)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.describe(ConsumerGroupCommand.scala:303)
	at kafka.admin.ConsumerGroupCommand$.main(ConsumerGroupCommand.scala:65)
	at kafka.admin.ConsumerGroupCommand.main(ConsumerGroupCommand.scala)

`
	if _, err := DefaultDescribeGroupParser().Parse(CommandOutput{Stdout: cannotConnectOutput}); err == nil {
		t.Error("Expected to get an error due to internal error in Kafka script.")
	}
}

// Test Kafka 10.0.0 script unable to connect to list groups.
func TestKafka10_0_0ScriptListGroupsErrorOutput(t *T) {
	cannotConnectOutput := `Error while executing consumer group command Request METADATA failed on brokers List(localhost:9042 (id: -1 rack: null))
java.lang.RuntimeException: Request METADATA failed on brokers List(localhost:9042 (id: -1 rack: null))
	at kafka.admin.AdminClient.sendAnyNode(AdminClient.scala:67)
	at kafka.admin.AdminClient.findAllBrokers(AdminClient.scala:87)
	at kafka.admin.AdminClient.listAllGroups(AdminClient.scala:96)
	at kafka.admin.AdminClient.listAllGroupsFlattened(AdminClient.scala:117)
	at kafka.admin.AdminClient.listAllConsumerGroupsFlattened(AdminClient.scala:121)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.list(ConsumerGroupCommand.scala:311)
	at kafka.admin.ConsumerGroupCommand$.main(ConsumerGroupCommand.scala:63)
	at kafka.admin.ConsumerGroupCommand.main(ConsumerGroupCommand.scala)

`
	if _, err := DefaultDescribeGroupParser().Parse(CommandOutput{Stdout: cannotConnectOutput}); err == nil {
		t.Error("Expected to get an error due to internal error in Kafka script.")
	}
}

func TestInterfaceImplementation(t *T) {
	var _ exporter.ConsumerGroupInfoClient = (*ConsumerGroupsCommandClient)(nil)
}
<commit_msg>ref(parsing): use subtests<commit_after>package kafka

import (
	. "testing"

	exporter "github.com/kawamuray/prometheus-kafka-consumer-group-exporter"
)

func TestParsingPartitionTableForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Note: This will only show information about consumers that use the Java consumer API (non-ZooKeeper-based consumers).\n",
		Stdout: `TOPIC                          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG        CONSUMER-ID                                       HOST                           CLIENT-ID
xxx           4          21555284        22970821        1415537    -                                                 -                              -
yyy           1          496377          525680          29303      -                                                 -                              -
yyy           7          478173          507408          29235      -                                                 -                              -`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "xxx",
			PartitionID:     "4",
			CurrentOffset:   21555284,
			Lag:             1415537,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
		{
			Topic:           "yyy",
			PartitionID:     "1",
			CurrentOffset:   496377,
			Lag:             29303,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
		{
			Topic:           "yyy",
			PartitionID:     "7",
			CurrentOffset:   478173,
			Lag:             29235,
			ClientID:        "-",
			ConsumerAddress: "-",
		},
	}

	t.Run("kafka0_10_2_1DescribeGroupParser", func(t *T) {
		comparePartitionTable(t, kafka0_10_2_1DescribeGroupParser, output, expected)
	})
	t.Run("DefaultDescribeGroupParser", func(t *T) {
		comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
	})
}

func TestParsingNoActiveMemberErrorForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Consumer group '$groupId' has no active members.\n",
	}
	t.Run("kafka0_10_2_1DescribeGroupParser", func(t *T) {
		assertErrorParsing(t, kafka0_10_2_1DescribeGroupParser, output)
	})
	t.Run("DefaultDescribeGroupParser", func(t *T) {
		assertErrorParsing(t, DefaultDescribeGroupParser(), output)
	})
}

func TestParsingWhenRebalancingErrorForKafkaVersion0_10_2_1(t *T) {
	output := CommandOutput{
		Stderr: "Warning: Consumer group '$groupId' is rebalancing.\n",
	}
	t.Run("kafka0_10_2_1DescribeGroupParser", func(t *T) {
		assertErrorParsing(t, kafka0_10_2_1DescribeGroupParser, output)
	})
	t.Run("DefaultDescribeGroupParser", func(t *T) {
		assertErrorParsing(t, DefaultDescribeGroupParser(), output)
	})
}

func assertErrorParsing(t *T, parser DescribeGroupParser, output CommandOutput) {
	_, err := parser.Parse(output)
	if err == nil {
		t.Error("Expected an error.")
	}
}

func TestParsingPartitionTableForKafkaVersion0_10_0_1(t *T) {
	output := CommandOutput{
		Stdout: `GROUP                          TOPIC                          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             OWNER
foobar-consumer topic-A                      2          12345200        12345200        0               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.1
foobar-consumer topic-A                      1          45678335        45678337        2               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.2
foobar-consumer topic-A                      0          91011178        91011179        1               foobar-consumer-1-StreamThread-1-consumer_/192.168.1.3`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "topic-A",
			PartitionID:     "2",
			CurrentOffset:   12345200,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.1",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "1",
			CurrentOffset:   45678335,
			Lag:             2,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.2",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "0",
			CurrentOffset:   91011178,
			Lag:             1,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.3",
		},
	}

	t.Run("kafka0_10_0_1DescribeGroupParser", func(t *T) {
		comparePartitionTable(t, kafka0_10_0_1DescribeGroupParser, output, expected)
	})
	t.Run("DefaultDescribeGroupParser", func(t *T) {
		comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
	})
}

func TestParsingPartitionTableForKafkaVersion0_9_0_1(t *T) {
	output := CommandOutput{
		Stdout: `GROUP, TOPIC, PARTITION, CURRENT OFFSET, LOG END OFFSET, LAG, OWNER
foobar-consumer, topic-A, 2, 12344967, 12344973, 6, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.1
foobar-consumer, topic-A, 1, 45678117, 45678117, 0, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.2
foobar-consumer, topic-A, 0, 91011145, 91011145, 0, foobar-consumer-1-StreamThread-1-consumer_/192.168.1.3`,
	}

	expected := []exporter.PartitionInfo{
		{
			Topic:           "topic-A",
			PartitionID:     "2",
			CurrentOffset:   12344967,
			Lag:             6,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.1",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "1",
			CurrentOffset:   45678117,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.2",
		},
		{
			Topic:           "topic-A",
			PartitionID:     "0",
			CurrentOffset:   91011145,
			Lag:             0,
			ClientID:        "foobar-consumer-1-StreamThread-1-consumer",
			ConsumerAddress: "192.168.1.3",
		},
	}

	t.Run("kafka0_9_0_1DescribeGroupParser", func(t *T) {
		comparePartitionTable(t, kafka0_9_0_1DescribeGroupParser, output, expected)
	})
	t.Run("DefaultDescribeGroupParser", func(t *T) {
		comparePartitionTable(t, DefaultDescribeGroupParser(), output, expected)
	})
}

func comparePartitionTable(t *T, parser DescribeGroupParser, output CommandOutput, expected []exporter.PartitionInfo) {
	values, err := parser.Parse(output)
	if err != nil {
		t.Fatal("Failed parsing. Parser:", parser, "Error:", err)
	}

	if len(values) != len(expected) {
		t.Fatal("Not same lengths. Was:", len(values), "Was:", len(expected))
	}
	for i, value := range values {
		comparePartitionInfo(t, parser, value, expected[i])
	}
}

func comparePartitionInfo(t *T, parser DescribeGroupParser, value, expected exporter.PartitionInfo) {
	if value, expected := value.Topic, expected.Topic; expected != value {
		t.Error("Wrong topic. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.PartitionID, expected.PartitionID; expected != value {
		t.Error("Wrong PartitionID. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.CurrentOffset, expected.CurrentOffset; expected != value {
		t.Error("Wrong CurrentOffset. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.Lag, expected.Lag; expected != value {
		t.Error("Wrong Lag. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.ClientID, expected.ClientID; expected != value {
		t.Error("Wrong ClientID. Parser:", parser, "Expected:", expected, "Was:", value)
	}
	if value, expected := value.ConsumerAddress, expected.ConsumerAddress; expected != value {
		t.Error("Wrong ConsumerAddress. Parser:", parser, "Expected:", expected, "Was:", value)
	}
}

// Test Kafka 10.0.0 script unable to connect to describe group.
func TestKafka10_0_0ScriptDescribeGroupErrorOutput(t *T) {
	cannotConnectOutput := `Error while executing consumer group command Request GROUP_COORDINATOR failed on brokers List(localhost:9042 (id: -1 rack: null))
java.lang.RuntimeException: Request GROUP_COORDINATOR failed on brokers List(localhost:9042 (id: -1 rack: null))
	at kafka.admin.AdminClient.sendAnyNode(AdminClient.scala:67)
	at kafka.admin.AdminClient.findCoordinator(AdminClient.scala:72)
	at kafka.admin.AdminClient.describeGroup(AdminClient.scala:125)
	at kafka.admin.AdminClient.describeConsumerGroup(AdminClient.scala:147)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.describeGroup(ConsumerGroupCommand.scala:315)
	at kafka.admin.ConsumerGroupCommand$ConsumerGroupService$class.describe(ConsumerGroupCommand.scala:86)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.describe(ConsumerGroupCommand.scala:303)
	at kafka.admin.ConsumerGroupCommand$.main(ConsumerGroupCommand.scala:65)
	at kafka.admin.ConsumerGroupCommand.main(ConsumerGroupCommand.scala)

`
	if _, err := DefaultDescribeGroupParser().Parse(CommandOutput{Stdout: cannotConnectOutput}); err == nil {
		t.Error("Expected to get an error due to internal error in Kafka script.")
	}
}

// Test Kafka 10.0.0 script unable to connect to list groups.
func TestKafka10_0_0ScriptListGroupsErrorOutput(t *T) {
	cannotConnectOutput := `Error while executing consumer group command Request METADATA failed on brokers List(localhost:9042 (id: -1 rack: null))
java.lang.RuntimeException: Request METADATA failed on brokers List(localhost:9042 (id: -1 rack: null))
	at kafka.admin.AdminClient.sendAnyNode(AdminClient.scala:67)
	at kafka.admin.AdminClient.findAllBrokers(AdminClient.scala:87)
	at kafka.admin.AdminClient.listAllGroups(AdminClient.scala:96)
	at kafka.admin.AdminClient.listAllGroupsFlattened(AdminClient.scala:117)
	at kafka.admin.AdminClient.listAllConsumerGroupsFlattened(AdminClient.scala:121)
	at kafka.admin.ConsumerGroupCommand$KafkaConsumerGroupService.list(ConsumerGroupCommand.scala:311)
	at kafka.admin.ConsumerGroupCommand$.main(ConsumerGroupCommand.scala:63)
	at kafka.admin.ConsumerGroupCommand.main(ConsumerGroupCommand.scala)

`
	if _, err := DefaultDescribeGroupParser().Parse(CommandOutput{Stdout: cannotConnectOutput}); err == nil {
		t.Error("Expected to get an error due to internal error in Kafka script.")
	}
}

func TestInterfaceImplementation(t *T) {
	var _ exporter.ConsumerGroupInfoClient = (*ConsumerGroupsCommandClient)(nil)
}
<|endoftext|>


<commit_before>package statsplugin

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

var statsStartTime = time.Now()

func getDurationString(duration time.Duration) string {
	return fmt.Sprintf(
		"%0.2d:%02d:%02d",
		int(duration.Hours()),
		int(duration.Minutes())%60,
		int(duration.Seconds())%60,
	)
}

// StatsCommand returns bot statistics.
func StatsCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) {
	stats := runtime.MemStats{}
	runtime.ReadMemStats(&stats)

	w := &tabwriter.Writer{}
	buf := &bytes.Buffer{}

	w.Init(buf, 0, 4, 0, ' ', 0)
	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "```\n")
	}
	fmt.Fprintf(w, "Bruxism: \t%s\n", bruxism.VersionString)
	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "Discordgo: \t%s\n", discordgo.VERSION)
	}
	fmt.Fprintf(w, "Go: \t%s\n", runtime.Version())
	fmt.Fprintf(w, "Uptime: \t%s\n", getDurationString(time.Now().Sub(statsStartTime)))
	fmt.Fprintf(w, "Memory used: \t%s / %s (%s garbage collected)\n", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.Sys), humanize.Bytes(stats.TotalAlloc))
	fmt.Fprintf(w, "Concurrent tasks: \t%d\n", runtime.NumGoroutine())
	if service.Name() == bruxism.DiscordServiceName {
		discord := service.(*bruxism.Discord)
		fmt.Fprintf(w, "Connected servers: \t%d\n", service.ChannelCount())
		shards := 0
		for _, s := range discord.Sessions {
			if s.DataReady {
				shards++
			}
		}
		if shards == len(discord.Sessions) {
			fmt.Fprintf(w, "Shards: \t%d\n", shards)
		} else {
			fmt.Fprintf(w, "Shards: \t%d (%d connected)\n", len(discord.Sessions), shards)
		}
		guild, err := discord.Channel(message.Channel())
		if err == nil {
			id, err := strconv.Atoi(guild.ID)
			if err == nil {
				fmt.Fprintf(w, "Current shard: \t%d\n", (id>>22)%len(discord.Sessions))
			}
		}
	} else {
		fmt.Fprintf(w, "Connected channels: \t%d\n", service.ChannelCount())
	}

	plugins := bot.Services[service.Name()].Plugins
	names := []string{}
	for _, plugin := range plugins {
		names = append(names, plugin.Name())
		sort.Strings(names)
	}

	for _, name := range names {
		stats := plugins[name].Stats(bot, service, message)
		for _, stat := range stats {
			fmt.Fprint(w, stat)
		}
	}

	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "\n```")
	}

	w.Flush()
	out := buf.String()

	if IsSeptapus {
		out += "\nSeptapus community: <https://discord.gg/HWN9pwj>\nPatreon: <https://www.patreon.com/iopred>\nBuilt with love by iopred."
	}

	if service.SupportsMultiline() {
		service.SendMessage(message.Channel(), out)
	} else {
		lines := strings.Split(out, "\n")
		for _, line := range lines {
			if err := service.SendMessage(message.Channel(), line); err != nil {
				break
			}
		}
	}
}

var IsSeptapus bool = false

// StatsHelp is the help for the stats command.
var StatsHelp = bruxism.NewCommandHelp("", "Lists bot statistics.")
<commit_msg>Only display shards when the bot is sharded. 1 index shards.<commit_after>package statsplugin

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

var statsStartTime = time.Now()

func getDurationString(duration time.Duration) string {
	return fmt.Sprintf(
		"%0.2d:%02d:%02d",
		int(duration.Hours()),
		int(duration.Minutes())%60,
		int(duration.Seconds())%60,
	)
}

// StatsCommand returns bot statistics.
func StatsCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) {
	stats := runtime.MemStats{}
	runtime.ReadMemStats(&stats)

	w := &tabwriter.Writer{}
	buf := &bytes.Buffer{}

	w.Init(buf, 0, 4, 0, ' ', 0)
	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "```\n")
	}
	fmt.Fprintf(w, "Bruxism: \t%s\n", bruxism.VersionString)
	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "Discordgo: \t%s\n", discordgo.VERSION)
	}
	fmt.Fprintf(w, "Go: \t%s\n", runtime.Version())
	fmt.Fprintf(w, "Uptime: \t%s\n", getDurationString(time.Now().Sub(statsStartTime)))
	fmt.Fprintf(w, "Memory used: \t%s / %s (%s garbage collected)\n", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.Sys), humanize.Bytes(stats.TotalAlloc))
	fmt.Fprintf(w, "Concurrent tasks: \t%d\n", runtime.NumGoroutine())
	if service.Name() == bruxism.DiscordServiceName {
		discord := service.(*bruxism.Discord)
		fmt.Fprintf(w, "Connected servers: \t%d\n", service.ChannelCount())
		if len(discord.Sessions) > 1 {
			shards := 0
			for _, s := range discord.Sessions {
				if s.DataReady {
					shards++
				}
			}
			if shards == len(discord.Sessions) {
				fmt.Fprintf(w, "Shards: \t%d\n", shards)
			} else {
				fmt.Fprintf(w, "Shards: \t%d (%d connected)\n", len(discord.Sessions), shards)
			}
			guild, err := discord.Channel(message.Channel())
			if err == nil {
				id, err := strconv.Atoi(guild.ID)
				if err == nil {
					fmt.Fprintf(w, "Current shard: \t%d\n", ((id>>22)%len(discord.Sessions) + 1))
				}
			}
		}
	} else {
		fmt.Fprintf(w, "Connected channels: \t%d\n", service.ChannelCount())
	}

	plugins := bot.Services[service.Name()].Plugins
	names := []string{}
	for _, plugin := range plugins {
		names = append(names, plugin.Name())
		sort.Strings(names)
	}

	for _, name := range names {
		stats := plugins[name].Stats(bot, service, message)
		for _, stat := range stats {
			fmt.Fprint(w, stat)
		}
	}

	if service.Name() == bruxism.DiscordServiceName {
		fmt.Fprintf(w, "\n```")
	}

	w.Flush()
	out := buf.String()

	if IsSeptapus {
		out += "\nSeptapus community: <https://discord.gg/HWN9pwj>\nPatreon: <https://www.patreon.com/iopred>\nBuilt with love by iopred."
	}

	if service.SupportsMultiline() {
		service.SendMessage(message.Channel(), out)
	} else {
		lines := strings.Split(out, "\n")
		for _, line := range lines {
			if err := service.SendMessage(message.Channel(), line); err != nil {
				break
			}
		}
	}
}

var IsSeptapus bool = false

// StatsHelp is the help for the stats command.
var StatsHelp = bruxism.NewCommandHelp("", "Lists bot statistics.")
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

type NodeType int

const (
	ndError NodeType = iota
	ndBlock
	ndCharacter
	ndExtVarDecl
	ndExtVarInit
	ndFunction
	ndInteger
	ndVarDecl
)

type Node interface {
	Type() NodeType
	String() string
}

// '{' node* '}'
type BlockNode struct {
	nodes []Node
}

func (b BlockNode) Type() NodeType { return ndBlock }
func (b BlockNode) String() string {
	str := "{\n"

	for _, node := range b.nodes {
		str += fmt.Sprintf("\t%v\n", node)
	}

	str += "}"
	return str
}

type CharacterNode struct {
	value string
}

func (c CharacterNode) Type() NodeType { return ndCharacter }
func (c CharacterNode) String() string { return fmt.Sprintf("'%s'", c.value) }

type ExternVarDeclNode struct {
	names []string
}

func (e ExternVarDeclNode) Type() NodeType { return ndExtVarDecl }
func (e ExternVarDeclNode) String() string {
	return fmt.Sprintf("extrn %s;", strings.Join(e.names, ", "))
}

// name value ';'
type ExternVarInitNode struct {
	name  string
	value Node
}

func (e ExternVarInitNode) Type() NodeType { return ndExtVarInit }
func (e ExternVarInitNode) String() string {
	return fmt.Sprintf("%s %v;", e.name, e.value)
}

// name '(' (var (',' var)*) ? ')' block
type FunctionNode struct {
	name   string
	params []string
	block  BlockNode
}

func (f FunctionNode) Type() NodeType { return ndFunction }
func (f FunctionNode) String() string {
	return fmt.Sprintf("%s(%s) %s",
		f.name, strings.Join(f.params, ", "), f.block)
}

type IntegerNode struct {
	value string
}

func (i IntegerNode) Type() NodeType { return ndInteger }
func (i IntegerNode) String() string { return i.value }

type VarDeclNode struct {
	vars []string
}

func (v VarDeclNode) Type() NodeType { return ndVarDecl }
func (v VarDeclNode) String() string {
	return fmt.Sprintf("auto %s;", strings.Join(v.vars, ", "))
}
<commit_msg>Add StringNode<commit_after>package main

import (
	"fmt"
	"strings"
)

type NodeType int

const (
	ndError NodeType = iota
	ndBlock
	ndCharacter
	ndExtVarDecl
	ndExtVarInit
	ndFunction
	ndInteger
	ndString
	ndVarDecl
)

type Node interface {
	Type() NodeType
	String() string
}

// '{' node* '}'
type BlockNode struct {
	nodes []Node
}

func (b BlockNode) Type() NodeType { return ndBlock }
func (b BlockNode) String() string {
	str := "{\n"

	for _, node := range b.nodes {
		str += fmt.Sprintf("\t%v\n", node)
	}

	str += "}"
	return str
}

type CharacterNode struct {
	value string
}

func (c CharacterNode) Type() NodeType { return ndCharacter }
func (c CharacterNode) String() string { return fmt.Sprintf("'%s'", c.value) }

type ExternVarDeclNode struct {
	names []string
}

func (e ExternVarDeclNode) Type() NodeType { return ndExtVarDecl }
func (e ExternVarDeclNode) String() string {
	return fmt.Sprintf("extrn %s;", strings.Join(e.names, ", "))
}

// name value ';'
type ExternVarInitNode struct {
	name  string
	value Node
}

func (e ExternVarInitNode) Type() NodeType { return ndExtVarInit }
func (e ExternVarInitNode) String() string {
	return fmt.Sprintf("%s %v;", e.name, e.value)
}

// name '(' (var (',' var)*) ? ')' block
type FunctionNode struct {
	name   string
	params []string
	block  BlockNode
}

func (f FunctionNode) Type() NodeType { return ndFunction }
func (f FunctionNode) String() string {
	return fmt.Sprintf("%s(%s) %s",
		f.name, strings.Join(f.params, ", "), f.block)
}

type IntegerNode struct {
	value string
}

func (i IntegerNode) Type() NodeType { return ndInteger }
func (i IntegerNode) String() string { return i.value }

type VarDeclNode struct {
	vars []string
}

type StringNode struct {
	value string
}

func (s StringNode) Type() NodeType { return ndString }
func (s StringNode) String() string { return fmt.Sprintf("\"%s\"", s.value) }

func (v VarDeclNode) Type() NodeType { return ndVarDecl }
func (v VarDeclNode) String() string {
	return fmt.Sprintf("auto %s;", strings.Join(v.vars, ", "))
}
<|endoftext|>


<commit_before>package elastigo

type NodeStatsResponse struct {
	ClusterName string `json:"cluster_name"`
	Nodes       map[string]NodeStatsNodeResponse
}

type NodeStatsNodeResponse struct {
	Name             string                                     `json:"name"`
	Timestamp        int64                                      `json:"timestamp"`
	TransportAddress string                                     `json:"transport_address"`
	Hostname         string                                     `json:"hostname"`
	Indices          NodeStatsIndicesResponse                   `json:"indices"`
	OS               NodeStatsOSResponse                        `json:"os"`
	Network          NodeStatsNetworkResponse                   `json:"network"`
	FS               NodeStatsFSResponse                        `json:"fs"`
	ThreadPool       map[string]NodeStatsThreadPoolPoolResponse `json:"thread_pool"`
}

type NodeStatsNetworkResponse struct {
	TCP NodeStatsTCPResponse `json:"tcp"`
}

type NodeStatsTransportResponse struct {
	ServerOpen int64 `json:"server_open"`
	RxCount    int64 `json:"rx_count"`
	RxSize     int64 `json:"rx_size_in_bytes"`
	TxCount    int64 `json:"tx_count"`
	TxSize     int64 `json:"tx_size_in_bytes"`
}

type NodeStatsThreadPoolPoolResponse struct {
	Threads   int64 `json:"threads"`
	Queue     int64 `json:"queue"`
	Active    int64 `json:"active"`
	Rejected  int64 `json:"rejected"`
	Largest   int64 `json:"largest"`
	Completed int64 `json:"completed"`
}

type NodeStatsTCPResponse struct {
	ActiveOpens  int64 `json:"active_opens"`
	PassiveOpens int64 `json:"passive_opens"`
	CurrEstab    int64 `json:"curr_estab"`
	InSegs       int64 `json:"in_segs"`
	OutSegs      int64 `json:"out_segs"`
	RetransSegs  int64 `json:"retrans_segs"`
	EstabResets  int64 `json:"estab_resets"`
	AttemptFails int64 `json:"attempt_fails"`
	InErrs       int64 `json:"in_errs"`
	OutRsts      int64 `json:"out_rsts"`
}

type NodeStatsIndicesResponse struct {
	Docs     NodeStatsIndicesDocsResponse
	Store    NodeStatsIndicesStoreResponse
	Indexing NodeStatsIndicesIndexingResponse
	Get      NodeStatsIndicesGetResponse
	Search   NodeStatsIndicesSearchResponse
}

type NodeStatsIndicesDocsResponse struct {
	Count   int64 `json:"count"`
	Deleted int64 `json:"deleted"`
}

type NodeStatsIndicesStoreResponse struct {
	Size         int64 `json:"size_in_bytes"`
	ThrottleTime int64 `json:"throttle_time_in_millis"`
}

type NodeStatsIndicesIndexingResponse struct {
	IndexTotal    int64 `json:"index_total"`
	IndexTime     int64 `json:"index_time_in_millis"`
	IndexCurrent  int64 `json:"index_current"`
	DeleteTotal   int64 `json:"delete_total"`
	DeleteTime    int64 `json:"delete_time_in_millis"`
	DeleteCurrent int64 `json:"delete_current"`
}

type NodeStatsIndicesGetResponse struct {
	Total        int64 `json:"total"`
	Time         int64 `json:"time_in_millis"`
	ExistsTotal  int64 `json:"exists_total"`
	ExistsTime   int64 `json:"exists_time_in_millis"`
	MissingTotal int64 `json:"missing_total"`
	MissingTime  int64 `json:"missing_time_in_millis"`
	Current      int64 `json:"current"`
}

type NodeStatsIndicesSearchResponse struct {
	OpenContext  int64 `json:"open_contexts"`
	QueryTotal   int64 `json:"query_total"`
	QueryTime    int64 `json:"query_time_in_millis"`
	QueryCurrent int64 `json:"query_current"`
	FetchTotal   int64 `json:"fetch_total"`
	FetchTime    int64 `json:"fetch_time_in_millis"`
	FetchCurrent int64 `json:"fetch_current"`
}

type NodeStatsOSResponse struct {
	Timestamp int64                   `json:"timestamp"`
	Uptime    int64                   `json:"uptime_in_millis"`
	LoadAvg   []float64               `json:"load_average"`
	CPU       NodeStatsOSCPUResponse  `json:"cpu"`
	Mem       NodeStatsOSMemResponse  `json:"mem"`
	Swap      NodeStatsOSSwapResponse `json:"swap"`
}

type NodeStatsOSMemResponse struct {
	Free       int64 `json:"free_in_bytes"`
	Used       int64 `json:"used_in_bytes"`
	ActualFree int64 `json:"actual_free_in_bytes"`
	ActualUsed int64 `json:"actual_used_in_bytes"`
}

type NodeStatsOSSwapResponse struct {
	Used int64 `json:"used_in_bytes"`
	Free int64 `json:"free_in_bytes"`
}

type NodeStatsOSCPUResponse struct {
	Sys   int64 `json:"sys"`
	User  int64 `json:"user"`
	Idle  int64 `json:"idle"`
	Steal int64 `json:"stolen"`
}

type NodeStatsProcessResponse struct {
	Timestamp int64                       `json:"timestamp"`
	OpenFD    int64                       `json:"open_file_descriptors"`
	CPU       NodeStatsProcessCPUResponse `json:"cpu"`
	Memory    NodeStatsProcessMemResponse `json:"mem"`
}

type NodeStatsProcessMemResponse struct {
	Resident     int64 `json:"resident_in_bytes"`
	Share        int64 `json:"share_in_bytes"`
	TotalVirtual int64 `json:"total_virtual_in_bytes"`
}

type NodeStatsProcessCPUResponse struct {
	Percent int64 `json:"percent"`
	Sys     int64 `json:"sys_in_millis"`
	User    int64 `json:"user_in_millis"`
	Total   int64 `json:"total_in_millis"`
}

type NodeStatsHTTPResponse struct {
	CurrentOpen int64 `json:"current_open"`
	TotalOpen   int64 `json:"total_open"`
}

type NodeStatsFSResponse struct {
	Timestamp int64                     `json:"timestamp"`
	Data      []NodeStatsFSDataResponse `json:"data"`
}

type NodeStatsFSDataResponse struct {
	Path          string `json:"path"`
	Mount         string `json:"mount"`
	Device        string `json:"dev"`
	Total         int64  `json:"total_in_bytes"`
	Free          int64  `json:"free_in_bytes"`
	Available     int64  `json:"available_in_bytes"`
	DiskReads     int64  `json:"disk_reads"`
	DiskWrites    int64  `json:"disk_writes"`
	DiskReadSize  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize int64  `json:"disk_write_size_in_bytes"`
}
<commit_msg>Filled the response a bit more<commit_after>package elastigo

type NodeStatsResponse struct {
	ClusterName string `json:"cluster_name"`
	Nodes       map[string]NodeStatsNodeResponse
}

type NodeStatsNodeResponse struct {
	Name             string                                     `json:"name"`
	Timestamp        int64                                      `json:"timestamp"`
	TransportAddress string                                     `json:"transport_address"`
	Hostname         string                                     `json:"hostname"`
	Host             string                                     `json:"host"`
	IP               []string                                   `json:"ip"`
	Attributes       NodeStatsNodeAttributes                    `json:"attributes"`
	Indices          NodeStatsIndicesResponse                   `json:"indices"`
	OS               NodeStatsOSResponse                        `json:"os"`
	Process          NodeStatsProcessResponse                   `json:"process"`
	JVM              NodeStatsJVMResponse                       `json:"jvm"`
	Network          NodeStatsNetworkResponse                   `json:"network"`
	FS               NodeStatsFSResponse                        `json:"fs"`
	ThreadPool       map[string]NodeStatsThreadPoolPoolResponse `json:"thread_pool"`
	Transport        NodeStatsTransportResponse                 `json:"transport"`
	FieldDataBreaker NodeStatsFieldDataBreakerResponse          `json:"fielddata_breaker"`
}

type NodeStatsNodeAttributes struct {
	Data   bool
	Client bool
}
type NodeStatsNetworkResponse struct {
	TCP NodeStatsTCPResponse `json:"tcp"`
}

type NodeStatsFieldDataBreakerResponse struct {
	MaximumSizeInBytes   int64 `json:"maximum_size_in_bytes"`
	MaximumSize          int64 `json:"maximum_size"`
	EstimatedSizeInBytes int64 `json:"estimated_size_in_bytes"`
	EstimatedSize        int64 `json:"estimated_size"`
	Overhead             int64 `json:"overhead"`
	Tripped              int64 `json:"tripped"`
}
type NodeStatsTransportResponse struct {
	ServerOpen int64 `json:"server_open"`
	RxCount    int64 `json:"rx_count"`
	RxSize     int64 `json:"rx_size_in_bytes"`
	TxCount    int64 `json:"tx_count"`
	TxSize     int64 `json:"tx_size_in_bytes"`
}

type NodeStatsThreadPoolPoolResponse struct {
	Threads   int64 `json:"threads"`
	Queue     int64 `json:"queue"`
	Active    int64 `json:"active"`
	Rejected  int64 `json:"rejected"`
	Largest   int64 `json:"largest"`
	Completed int64 `json:"completed"`
}

type NodeStatsTCPResponse struct {
	ActiveOpens  int64 `json:"active_opens"`
	PassiveOpens int64 `json:"passive_opens"`
	CurrEstab    int64 `json:"curr_estab"`
	InSegs       int64 `json:"in_segs"`
	OutSegs      int64 `json:"out_segs"`
	RetransSegs  int64 `json:"retrans_segs"`
	EstabResets  int64 `json:"estab_resets"`
	AttemptFails int64 `json:"attempt_fails"`
	InErrs       int64 `json:"in_errs"`
	OutRsts      int64 `json:"out_rsts"`
}

type NodeStatsIndicesResponse struct {
	Docs        NodeStatsIndicesDocsResponse        `json:"docs"`
	Store       NodeStatsIndicesStoreResponse       `json:"store"`
	Indexing    NodeStatsIndicesIndexingResponse    `json:"indexing"`
	Get         NodeStatsIndicesGetResponse         `json:"get"`
	Search      NodeStatsIndicesSearchResponse      `json:"search"`
	Merges      NodeStatsIndicesMergesResponse      `json:"merges"`
	Refresh     NodeStatsIndicesRefreshResponse     `json:"refresh"`
	Flush       NodeStatsIndicesFlushResponse       `json:"flush"`
	Warmer      NodeStatsIndicesWarmerResponse      `json:"warmer"`
	FilterCache NodeStatsIndicesFilterCacheResponse `json:"filter_cache"`
	IdCache     NodeStatsIndicesIdCacheResponse     `json:"id_cache"`
	FieldData   NodeStatsIndicesFieldDataResponse   `json:"fielddata"`
	Percolate   NodeStatsIndicesPercolateResponse   `json:"percolate"`
	Completion  NodeStatsIndicesCompletionResponse  `json:"completion"`
	Segments    NodeStatsIndicesSegmentsResponse    `json:"segments"`
	Translog    NodeStatsIndicesTranslogResponse    `json:"translog"`
	Suggest     NodeStatsIndicesSuggestResponse     `json:"suggest"`
}

type NodeStatsIndicesDocsResponse struct {
	Count   int64 `json:"count"`
	Deleted int64 `json:"deleted"`
}

type NodeStatsIndicesStoreResponse struct {
	Size         int64 `json:"size_in_bytes"`
	ThrottleTime int64 `json:"throttle_time_in_millis"`
}

type NodeStatsIndicesIndexingResponse struct {
	IndexTotal    int64 `json:"index_total"`
	IndexTime     int64 `json:"index_time_in_millis"`
	IndexCurrent  int64 `json:"index_current"`
	DeleteTotal   int64 `json:"delete_total"`
	DeleteTime    int64 `json:"delete_time_in_millis"`
	DeleteCurrent int64 `json:"delete_current"`
}

type NodeStatsIndicesGetResponse struct {
	Total        int64 `json:"total"`
	Time         int64 `json:"time_in_millis"`
	ExistsTotal  int64 `json:"exists_total"`
	ExistsTime   int64 `json:"exists_time_in_millis"`
	MissingTotal int64 `json:"missing_total"`
	MissingTime  int64 `json:"missing_time_in_millis"`
	Current      int64 `json:"current"`
}

type NodeStatsIndicesSearchResponse struct {
	OpenContext  int64 `json:"open_contexts"`
	QueryTotal   int64 `json:"query_total"`
	QueryTime    int64 `json:"query_time_in_millis"`
	QueryCurrent int64 `json:"query_current"`
	FetchTotal   int64 `json:"fetch_total"`
	FetchTime    int64 `json:"fetch_time_in_millis"`
	FetchCurrent int64 `json:"fetch_current"`
}
type NodeStatsIndicesMergesResponse struct {
	Current            int64 `json:"current"`
	CurrentDocs        int64 `json:"current_docs"`
	CurrentSizeInBytes int64 `json:"current_size_in_bytes"`
	Total              int64 `json:"total"`
	TotalTimeInMs      int64 `json:"total_time_in_millis"`
	TotalDocs          int64 `json:"total_docs"`
	TotalSizeInBytes   int64 `json:"total_size_in_bytes"`
}
type NodeStatsIndicesRefreshResponse struct {
	Total         int64 `json:"total"`
	TotalTimeInMs int64 `json:"total_time_in_millis"`
}
type NodeStatsIndicesFlushResponse struct {
	Total         int64 `json:"total"`
	TotalTimeInMs int64 `json:"total_time_in_millis"`
}
type NodeStatsIndicesWarmerResponse struct {
	Current       int64 `json:"current"`
	Total         int64 `json:"total"`
	TotalTimeInMs int64 `json:"total_time_in_millis"`
}
type NodeStatsIndicesFilterCacheResponse struct {
	MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
	Evictions         int64 `json:"evictions"`
}
type NodeStatsIndicesIdCacheResponse struct {
	MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
}
type NodeStatsIndicesFieldDataResponse struct {
	MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
	Evictions         int64 `json:"evictions"`
}
type NodeStatsIndicesPercolateResponse struct {
	Total             int64 `json:"total"`
	TimeInMs          int64 `json:"time_in_millis"`
	Current           int64 `json:"current"`
	MemorySizeInBytes int64 `json:"memory_size_in_bytes"`
	MemorySize        int64 `json:"memory_size"`
	Queries           int64 `json:"queries"`
}
type NodeStatsIndicesCompletionResponse struct {
	SizeInBytes int64 `json:"size_in_bytes"`
}
type NodeStatsIndicesSegmentsResponse struct {
	Count                    int64 `json:"count"`
	MemoryInBytes            int64 `json:"memory_in_bytes"`
	IndexWriterMemoryInBytes int64 `json:"index_writer_memory_in_bytes"`
	VersionMapMemoryInBytes  int64 `json:"version_map_memory_in_bytes"`
}
type NodeStatsIndicesTranslogResponse struct {
	Operations  int64 `json:"operations"`
	SizeInBytes int64 `json:"size_in_bytes"`
}
type NodeStatsIndicesSuggestResponse struct {
	Total    int64 `json:"total"`
	TimeInMs int64 `json:"time_in_millis"`
	Current  int64 `json:"current"`
}
type NodeStatsOSResponse struct {
	Timestamp int64                   `json:"timestamp"`
	Uptime    int64                   `json:"uptime_in_millis"`
	LoadAvg   []float64               `json:"load_average"`
	CPU       NodeStatsOSCPUResponse  `json:"cpu"`
	Mem       NodeStatsOSMemResponse  `json:"mem"`
	Swap      NodeStatsOSSwapResponse `json:"swap"`
}

type NodeStatsOSMemResponse struct {
	Free       int64 `json:"free_in_bytes"`
	Used       int64 `json:"used_in_bytes"`
	ActualFree int64 `json:"actual_free_in_bytes"`
	ActualUsed int64 `json:"actual_used_in_bytes"`
}

type NodeStatsOSSwapResponse struct {
	Used int64 `json:"used_in_bytes"`
	Free int64 `json:"free_in_bytes"`
}

type NodeStatsOSCPUResponse struct {
	Sys   int64 `json:"sys"`
	User  int64 `json:"user"`
	Idle  int64 `json:"idle"`
	Steal int64 `json:"stolen"`
}

type NodeStatsProcessResponse struct {
	Timestamp int64                       `json:"timestamp"`
	OpenFD    int64                       `json:"open_file_descriptors"`
	CPU       NodeStatsProcessCPUResponse `json:"cpu"`
	Memory    NodeStatsProcessMemResponse `json:"mem"`
}

type NodeStatsProcessMemResponse struct {
	Resident     int64 `json:"resident_in_bytes"`
	Share        int64 `json:"share_in_bytes"`
	TotalVirtual int64 `json:"total_virtual_in_bytes"`
}

type NodeStatsProcessCPUResponse struct {
	Percent int64 `json:"percent"`
	Sys     int64 `json:"sys_in_millis"`
	User    int64 `json:"user_in_millis"`
	Total   int64 `json:"total_in_millis"`
}

type NodeStatsJVMResponse struct {
	Timestame   int64                                      `json:"timestamp"`
	UptimeInMs  int64                                      `json:"uptime_in_millis"`
	Mem         NodeStatsJVMMemResponse                    `json:"mem"`
	Threads     NodeStatsJVMThreadsResponse                `json:"threads"`
	GC          NodeStatsJVMGCResponse                     `json:"gc"`
	BufferPools map[string]NodeStatsJVMBufferPoolsResponse `json:"buffer_pools"`
}

type NodeStatsJVMMemResponse struct {
	HeapUsedInBytes         int64                                   `json:"heap_used_in_bytes"`
	HeapUsedPercent         int64                                   `json:"heap_used_percent"`
	HeapCommitedInBytes     int64                                   `json:"heap_commited_in_bytes"`
	HeapMaxInBytes          int64                                   `json:"heap_max_in_bytes"`
	NonHeapUsedInBytes      int64                                   `json:"non_heap_used_in_bytes"`
	NonHeapCommittedInBytes int64                                   `json:"non_heap_used_in_bytes"`
	Pools                   map[string]NodeStatsJVMMemPoolsResponse `json:"pools"`
}
type NodeStatsJVMMemPoolsResponse struct {
	UsedInBytes     int64 `json:"used_in_bytes"`
	MaxInBytes      int64 `json:"max_in_bytes"`
	PeakUsedInBytes int64 `json:"peak_used_in_bytes"`
	PeakMaxInBytes  int64 `json:"peak_max_in_bytes"`
}
type NodeStatsJVMThreadsResponse struct {
	Count     int64 `json:"count"`
	PeakCount int64 `json:"peak_count"`
}
type NodeStatsJVMGCResponse struct {
	Collectors map[string]NodeStatsJVMGCCollectorsAgeResponse `json:"collectors"`
}
type NodeStatsJVMGCCollectorsAgeResponse struct {
	Count    int64 `json:"collection_count"`
	TimeInMs int64 `json:"collection_time_in_millis"`
}
type NodeStatsJVMBufferPoolsResponse struct {
	Count                int64 `json:"count"`
	UsedInBytes          int64 `json:"used_in_bytes"`
	TotalCapacityInBytes int64 `json:"total_capacity_in_bytes"`
}
type NodeStatsHTTPResponse struct {
	CurrentOpen int64 `json:"current_open"`
	TotalOpen   int64 `json:"total_open"`
}

type NodeStatsFSResponse struct {
	Timestamp int64                     `json:"timestamp"`
	Data      []NodeStatsFSDataResponse `json:"data"`
}

type NodeStatsFSDataResponse struct {
	Path          string `json:"path"`
	Mount         string `json:"mount"`
	Device        string `json:"dev"`
	Total         int64  `json:"total_in_bytes"`
	Free          int64  `json:"free_in_bytes"`
	Available     int64  `json:"available_in_bytes"`
	DiskReads     int64  `json:"disk_reads"`
	DiskWrites    int64  `json:"disk_writes"`
	DiskReadSize  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize int64  `json:"disk_write_size_in_bytes"`
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/astaxie/beego"
)

type RepositoryController struct {
	beego.Controller
}

func (this *RepositoryController) Prepare() {
	this.Ctx.Output.Context.ResponseWriter.Header().Set("X-Docker-Registry-Version", beego.AppConfig.String("Version"))
	this.Ctx.Output.Context.ResponseWriter.Header().Set("X-Docker-Registry-Standalone", beego.AppConfig.String("Standalone"))
}

func (this *RepositoryController) PUTRepository() {

}

func (this *RepositoryController) PUTRepositoryImages() {

}

func (this *RepositoryController) GETRepositoryImages() {

}

func (this *RepositoryController) DELETERepositoryImages() {

}

func (this *RepositoryController) PUTRepositoryAuth() {

}

func (this *RepositoryController) PUTProperties() {

}

func (this *RepositoryController) GETProperties() {

}

func (this *RepositoryController) GETTags() {

}

func (this *RepositoryController) GETTag() {

}

func (this *RepositoryController) GETRepositoyJSON() {

}

func (this *RepositoryController) GETTagJSON() {

}

func (this *RepositoryController) PUTTag() {

}

func (this *RepositoryController) DELETETag() {

}

func (this *RepositoryController) DELETERepository() {

}

func (this *RepositoryController) DELETERepositoryTags() {

}
<commit_msg>Comment repository controller.<commit_after>package controllers

import (
	"github.com/astaxie/beego"
)

type RepositoryController struct {
	beego.Controller
}

func (this *RepositoryController) Prepare() {
	this.Ctx.Output.Context.ResponseWriter.Header().Set("X-Docker-Registry-Version", beego.AppConfig.String("Version"))
	this.Ctx.Output.Context.ResponseWriter.Header().Set("X-Docker-Registry-Standalone", beego.AppConfig.String("Standalone"))
}

// http://docs.docker.io/en/latest/reference/api/registry_api/#tags
// GET /v1/repositories/(namespace)/(repository)/tags
// get all of the tags for the given repo.
// Example Request:
//    GET /v1/repositories/foo/bar/tags HTTP/1.1
//    Host: registry-1.docker.io
//    Accept: application/json
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    Cookie: (Cookie provided by the Registry)
// Parameters:
//    namespace – namespace for the repo
//    repository – name for the repo
// Example Response:
//    HTTP/1.1 200
//    Vary: Accept
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    {
//      "latest": "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f",
//      "0.1.1":  "b486531f9a779a0c17e3ed29dae8f12c4f9e89cc6f0bc3c38722009fe6857087"
//    }
// Status Codes:
//    200 – OK
//    401 – Requires authorization
//    404 – Repository not found
func (this *RepositoryController) GETTags() {

}

// http://docs.docker.io/en/latest/reference/api/registry_api/#tags
// GET /v1/repositories/(namespace)/(repository)/tags/(tag)
// get a tag for the given repo.
// Example Request:
//    GET /v1/repositories/foo/bar/tags/latest HTTP/1.1
//    Host: registry-1.docker.io
//    Accept: application/json
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    Cookie: (Cookie provided by the Registry)
// Parameters:
//    namespace – namespace for the repo
//    repository – name for the repo
//    tag – name of tag you want to get
// Example Response:
//    HTTP/1.1 200
//    Vary: Accept
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
// Status Codes:
//    200 – OK
//    401 – Requires authorization
//    404 – Tag not found
func (this *RepositoryController) GETTag() {

}

// http://docs.docker.io/en/latest/reference/api/registry_api/#tags
// DELETE /v1/repositories/(namespace)/(repository)/tags/(tag)
// delete the tag for the repo
// Example Request:
//    DELETE /v1/repositories/foo/bar/tags/latest HTTP/1.1
//    Host: registry-1.docker.io
//    Accept: application/json
//    Content-Type: application/json
//    Cookie: (Cookie provided by the Registry)
// Parameters:
//    namespace – namespace for the repo
//    repository – name for the repo
//    tag – name of tag you want to delete
// Example Response:
//    HTTP/1.1 200
//    Vary: Accept
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    ""
// Status Codes:
//    200 – OK
//    401 – Requires authorization
//    404 – Tag not found
func (this *RepositoryController) DELETETag() {

}

// http://docs.docker.io/en/latest/reference/api/registry_api/#tags
// PUT /v1/repositories/(namespace)/(repository)/tags/(tag)
// put a tag for the given repo.
// Example Request:
//    PUT /v1/repositories/foo/bar/tags/latest HTTP/1.1
//    Host: registry-1.docker.io
//    Accept: application/json
//    Content-Type: application/json
//    Cookie: (Cookie provided by the Registry)
//    "9e89cc6f0bc3c38722009fe6857087b486531f9a779a0c17e3ed29dae8f12c4f"
// Parameters:
//    namespace – namespace for the repo
//    repository – name for the repo
//    tag – name of tag you want to add
// Example Response:
//    HTTP/1.1 200
//    Vary: Accept
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    ""
// Status Codes:
//    200 – OK
//    400 – Invalid data
//    401 – Requires authorization
//    404 – Image not found
func (this *RepositoryController) PUTTag() {

}

// http://docs.docker.io/en/latest/reference/api/registry_api/#repositories
// DELETE /v1/repositories/(namespace)/(repository)/
// delete a repository
// Example Request:
//    DELETE /v1/repositories/foo/bar/ HTTP/1.1
//    Host: registry-1.docker.io
//    Accept: application/json
//    Content-Type: application/json
//    Cookie: (Cookie provided by the Registry)
//    ""
// Parameters:
//    namespace – namespace for the repo
//    repository – name for the repo
// Example Response:
//    HTTP/1.1 200
//    Vary: Accept
//    Content-Type: application/json
//    X-Docker-Registry-Version: 0.6.0
//    ""
// Status Codes:
//    200 – OK
//    401 – Requires authorization
//    404 – Repository not found
func (this *RepositoryController) DELETERepositoryImages() {

}

// http://docs.docker.io/en/latest/reference/api/index_api/#repository

func (this *RepositoryController) PUTRepository() {

}

func (this *RepositoryController) PUTRepositoryImages() {

}

func (this *RepositoryController) GETRepositoryImages() {

}

func (this *RepositoryController) PUTRepositoryAuth() {

}

func (this *RepositoryController) PUTProperties() {

}

func (this *RepositoryController) GETProperties() {

}

func (this *RepositoryController) GETRepositoyJSON() {

}

func (this *RepositoryController) GETTagJSON() {

}

func (this *RepositoryController) DELETERepository() {

}

func (this *RepositoryController) DELETERepositoryTags() {

}
<|endoftext|>


<commit_before>//Package backoff helps you at backing off !
//
//It was forked from github.com/cenkalti/backoff which is very awesome.
//
//I wanted a BackOff that'd just sleep and do nothing else.
package backoff

import "time"

// BackOffer interface to use after a retryable operation failed.
// A Backoffer.BackOff sleeps.
type BackOffer interface {
	// Example usage:
	//
	//   for ;; {
	//       err, canRetry := somethingThatCanFail()
	//       if err != nil && canRetry {
	//           backoffer.Backoff()
	//       }
	//   }
	BackOff()

	// Reset to initial state.
	Reset()
}

// Indicates that no more retries should be made for use in NextBackOff().
const Stop time.Duration = -1

// ZeroBackOff is a fixed back-off policy whose back-off time is always zero,
// meaning that the operation is retried immediately without waiting.
type ZeroBackOff struct{}

func (b *ZeroBackOff) Reset() {}

func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }

type ConstantBackOff struct {
	Interval time.Duration
}

func (b *ConstantBackOff) Reset()                     {}
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }

func NewConstantBackOff(d time.Duration) *ConstantBackOff {
	return &ConstantBackOff{Interval: d}
}
<commit_msg>refactor(zerobackoff): do not return time anymore<commit_after>//Package backoff helps you at backing off !
//
//It was forked from github.com/cenkalti/backoff which is very awesome.
//
//I wanted a BackOff that'd just sleep and do nothing else.
package backoff

import "time"

// BackOffer interface to use after a retryable operation failed.
// A Backoffer.BackOff sleeps.
type BackOffer interface {
	// Example usage:
	//
	//   for ;; {
	//       err, canRetry := somethingThatCanFail()
	//       if err != nil && canRetry {
	//           backoffer.Backoff()
	//       }
	//   }
	BackOff()

	// Reset to initial state.
	Reset()
}

// Indicates that no more retries should be made for use in NextBackOff().
const Stop time.Duration = -1

// ZeroBackOff is a fixed back-off policy whose back-off time is always zero,
// meaning that the operation is retried immediately without waiting.
type ZeroBackOff struct{}

var _ BackOffer = (*ZeroBackOff)(nil)

func (b *ZeroBackOff) Reset() {}

func (b *ZeroBackOff) BackOff() {}

type ConstantBackOff struct {
	Interval time.Duration
}

func (b *ConstantBackOff) Reset()                     {}
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }

func NewConstantBackOff(d time.Duration) *ConstantBackOff {
	return &ConstantBackOff{Interval: d}
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google Inc.
//
// 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 layout

import (
	"bufio"
	"context"
	"os"
	"runtime"
	"strings"

	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/host"
	"github.com/google/gapid/core/os/file"
)

const (
	ErrCannotFindPackageFiles = fault.Const("Cannot find package files")
	ErrUnknownABI             = fault.Const("Unknown device ABI: %+v")
)

// LibraryType enumerates the possible GAPID dynamic libraries.
type LibraryType int

const (
	LibGraphicsSpy LibraryType = iota
	LibVirtualSwapChain
)

// FileLayout provides a unified way of accessing various Gapid binaries.
type FileLayout interface {
	// Strings returns the path to the binary string table.
	Strings(ctx context.Context) (file.Path, error)
	// Gapit returns the path to the gapit binary in this layout.
	Gapit(ctx context.Context) (file.Path, error)
	// Gapis returns the path to the gapis binary in this layout.
	Gapis(ctx context.Context) (file.Path, error)
	// Gapir returns the path to the gapir binary in this layout.
	Gapir(ctx context.Context) (file.Path, error)
	// GapidApk returns the path to gapid.apk in this layout.
	GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error)
	// Library returns the path to the requested library.
	Library(ctx context.Context, lib LibraryType) (file.Path, error)
	// Json returns the path to the Vulkan layer JSON definition for the given library.
	Json(ctx context.Context, lib LibraryType) (file.Path, error)
	// GoArgs returns additional arguments to pass to go binaries.
	GoArgs(ctx context.Context) []string
}

func withExecutablePlatformSuffix(exe string) string {
	if runtime.GOOS == "windows" {
		return exe + ".exe"
	}
	return exe
}

var libTypeToName = map[LibraryType]string{
	LibGraphicsSpy:      withLibraryPlatformSuffix("libgapii"),
	LibVirtualSwapChain: withLibraryPlatformSuffix("libVkLayer_VirtualSwapchain"),
}

var libTypeToJson = map[LibraryType]string{
	LibGraphicsSpy:      "GraphicsSpyLayer.json",
	LibVirtualSwapChain: "VirtualSwapchainLayer.json",
}

func withLibraryPlatformSuffix(lib string) string {
	switch runtime.GOOS {
	case "windows":
		return lib + ".dll"
	case "darwin":
		return lib + ".dylib"
	default:
		return lib + ".so"
	}
}

var abiToApk = map[device.Architecture]string{
	device.ARMv7a: "gapid-armeabi.apk",
	device.ARMv8a: "gapid-aarch64.apk",
	device.X86:    "gapid-x86.apk",
}

// pkgLayout is the file layout used when running executables from a packaged
// build.
type pkgLayout struct {
	root file.Path
}

func (l pkgLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.root.Join("strings", "en-us.stb"), nil
}

func (l pkgLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapit")), nil
}

func (l pkgLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapir")), nil
}

func (l pkgLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapis")), nil
}

func (l pkgLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.root.Join(abiToApk[abi.Architecture]), nil
}

func (l pkgLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join("lib", libTypeToName[lib]), nil
}

func (l pkgLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join("lib", libTypeToJson[lib]), nil
}

func (l pkgLayout) GoArgs(ctx context.Context) []string {
	return nil
}

var binABIToDir = map[string]string{
	"armeabi":     "android-armv7a",
	"armeabi-v7a": "android-armv7a",
	"arm64-v8a":   "android-armv8a",
	"x86":         "android-x86",
}

var dirToBinABI map[string]string

func init() {
	dirToBinABI = make(map[string]string)
	for abi, dir := range binABIToDir {
		dirToBinABI[dir] = abi
	}
}

// DirToBinABI returns the ABI name that corresponds to the given binLayout Directory
func DirToBinABI(dir string) string {
	return dirToBinABI[dir]
}

// binLayout is the file layout used when running executables from the build's
// bin directory.
type binLayout struct {
	root file.Path
}

func abiDirectory(ctx context.Context, abi *device.ABI) (string, error) {
	dir, ok := binABIToDir[abi.Name]
	if !ok {
		return "", log.Errf(ctx, ErrUnknownABI, "ABI: %v", abi)
	}
	return dir, nil
}

func (l binLayout) file(ctx context.Context, abi *device.ABI, name string) (file.Path, error) {
	if abi.OS == host.Instance(ctx).Configuration.OS.Kind {
		return l.root.Join(name), nil
	}
	dir, err := abiDirectory(ctx, abi)
	if err != nil {
		return file.Path{}, err
	}
	return l.root.Join(dir, name), nil
}

func (l binLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.root.Join("strings", "en-us.stb"), nil
}

func (l binLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapit")), nil
}

func (l binLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapir")), nil
}

func (l binLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapis")), nil
}

func (l binLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.file(ctx, abi, abiToApk[abi.Architecture])
}

func (l binLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join(libTypeToName[lib]), nil
}

func (l binLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join(libTypeToJson[lib]), nil
}

func (l binLayout) GoArgs(ctx context.Context) []string {
	return nil
}

// runfilesLayout is a layout that uses the bazel generated runfiles manifest
// to find the various dependencies.
type runfilesLayout struct {
	manifest string
	mapping  map[string]string
}

var abiToApkPath = map[device.Architecture]string{
	device.ARMv7a: "arm64-v8a.apk",
	device.ARMv8a: "armeabi-v7a.apk",
	device.X86:    "x86.apk",
}

var libTypeToLibPath = map[LibraryType]string{
	LibGraphicsSpy:      withLibraryPlatformSuffix("gapid/gapii/cc/libgapii"),
	LibVirtualSwapChain: withLibraryPlatformSuffix("gapid/core/vulkan/vk_virtual_swapchain/cc/libVkLayer_VirtualSwapchain"),
}

var libTypeToJsonPath = map[LibraryType]string{
	LibGraphicsSpy:      "gapid/gapii/vulkan/vk_graphics_spy/cc/GraphicsSpyLayer.json",
	LibVirtualSwapChain: "gapid/core/vulkan/vk_virtual_swapchain/cc/VirtualSwapchainLayer.json",
}

// RunfilesLayout creates a new layout based on the given runfiles manifest.
func RunfilesLayout(manifest file.Path) (FileLayout, error) {
	file, err := os.Open(manifest.System())
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r := runfilesLayout{
		manifest: manifest.System(),
		mapping: make(map[string]string),
	}
	for scanner.Scan() {
		line := scanner.Text()
		if p := strings.IndexRune(line, ' '); p > 0 {
			key, value := line[:p], line[p+1:]
			r.mapping[key] = value
		}
	}

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

func (l *runfilesLayout) find(key string) (file.Path, error) {
	if r, ok := l.mapping[key]; ok {
		return file.Abs(r), nil
	}
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l *runfilesLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.find("gapid/gapis/messages/en-us.stb")
}

func (l *runfilesLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapit/gapit"))
}

func (l *runfilesLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapis/gapis"))
}

func (l *runfilesLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapir/cc/gapir"))
}

func (l *runfilesLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.find("gapid/gapidapk/android/apk/"+abiToApkPath[abi.Architecture])
}

func (l *runfilesLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.find(libTypeToLibPath[lib])
}

func (l *runfilesLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.find(libTypeToJsonPath[lib])
}

func (l *runfilesLayout) GoArgs(ctx context.Context) []string {
	return []string{"-runfiles", l.manifest}
}

// unknownLayout is the file layout used when no other layout can be discovered.
// All methods will return an error.
type unknownLayout struct{}

func (l unknownLayout) Strings(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapit(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapis(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapir(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) GoArgs(ctx context.Context) []string {
	return nil
}

// BinLayout returns a binLayout implementation of FileLayout rooted in the given directory.
func BinLayout(root file.Path) FileLayout {
	return binLayout{root}
}
<commit_msg>Fix the ABI to APK mapping for the runfiles layout.<commit_after>// Copyright (C) 2017 Google Inc.
//
// 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 layout

import (
	"bufio"
	"context"
	"os"
	"runtime"
	"strings"

	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/host"
	"github.com/google/gapid/core/os/file"
)

const (
	ErrCannotFindPackageFiles = fault.Const("Cannot find package files")
	ErrUnknownABI             = fault.Const("Unknown device ABI: %+v")
)

// LibraryType enumerates the possible GAPID dynamic libraries.
type LibraryType int

const (
	LibGraphicsSpy LibraryType = iota
	LibVirtualSwapChain
)

// FileLayout provides a unified way of accessing various Gapid binaries.
type FileLayout interface {
	// Strings returns the path to the binary string table.
	Strings(ctx context.Context) (file.Path, error)
	// Gapit returns the path to the gapit binary in this layout.
	Gapit(ctx context.Context) (file.Path, error)
	// Gapis returns the path to the gapis binary in this layout.
	Gapis(ctx context.Context) (file.Path, error)
	// Gapir returns the path to the gapir binary in this layout.
	Gapir(ctx context.Context) (file.Path, error)
	// GapidApk returns the path to gapid.apk in this layout.
	GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error)
	// Library returns the path to the requested library.
	Library(ctx context.Context, lib LibraryType) (file.Path, error)
	// Json returns the path to the Vulkan layer JSON definition for the given library.
	Json(ctx context.Context, lib LibraryType) (file.Path, error)
	// GoArgs returns additional arguments to pass to go binaries.
	GoArgs(ctx context.Context) []string
}

func withExecutablePlatformSuffix(exe string) string {
	if runtime.GOOS == "windows" {
		return exe + ".exe"
	}
	return exe
}

var libTypeToName = map[LibraryType]string{
	LibGraphicsSpy:      withLibraryPlatformSuffix("libgapii"),
	LibVirtualSwapChain: withLibraryPlatformSuffix("libVkLayer_VirtualSwapchain"),
}

var libTypeToJson = map[LibraryType]string{
	LibGraphicsSpy:      "GraphicsSpyLayer.json",
	LibVirtualSwapChain: "VirtualSwapchainLayer.json",
}

func withLibraryPlatformSuffix(lib string) string {
	switch runtime.GOOS {
	case "windows":
		return lib + ".dll"
	case "darwin":
		return lib + ".dylib"
	default:
		return lib + ".so"
	}
}

var abiToApk = map[device.Architecture]string{
	device.ARMv7a: "gapid-armeabi.apk",
	device.ARMv8a: "gapid-aarch64.apk",
	device.X86:    "gapid-x86.apk",
}

// pkgLayout is the file layout used when running executables from a packaged
// build.
type pkgLayout struct {
	root file.Path
}

func (l pkgLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.root.Join("strings", "en-us.stb"), nil
}

func (l pkgLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapit")), nil
}

func (l pkgLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapir")), nil
}

func (l pkgLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapis")), nil
}

func (l pkgLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.root.Join(abiToApk[abi.Architecture]), nil
}

func (l pkgLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join("lib", libTypeToName[lib]), nil
}

func (l pkgLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join("lib", libTypeToJson[lib]), nil
}

func (l pkgLayout) GoArgs(ctx context.Context) []string {
	return nil
}

var binABIToDir = map[string]string{
	"armeabi":     "android-armv7a",
	"armeabi-v7a": "android-armv7a",
	"arm64-v8a":   "android-armv8a",
	"x86":         "android-x86",
}

var dirToBinABI map[string]string

func init() {
	dirToBinABI = make(map[string]string)
	for abi, dir := range binABIToDir {
		dirToBinABI[dir] = abi
	}
}

// DirToBinABI returns the ABI name that corresponds to the given binLayout Directory
func DirToBinABI(dir string) string {
	return dirToBinABI[dir]
}

// binLayout is the file layout used when running executables from the build's
// bin directory.
type binLayout struct {
	root file.Path
}

func abiDirectory(ctx context.Context, abi *device.ABI) (string, error) {
	dir, ok := binABIToDir[abi.Name]
	if !ok {
		return "", log.Errf(ctx, ErrUnknownABI, "ABI: %v", abi)
	}
	return dir, nil
}

func (l binLayout) file(ctx context.Context, abi *device.ABI, name string) (file.Path, error) {
	if abi.OS == host.Instance(ctx).Configuration.OS.Kind {
		return l.root.Join(name), nil
	}
	dir, err := abiDirectory(ctx, abi)
	if err != nil {
		return file.Path{}, err
	}
	return l.root.Join(dir, name), nil
}

func (l binLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.root.Join("strings", "en-us.stb"), nil
}

func (l binLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapit")), nil
}

func (l binLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapir")), nil
}

func (l binLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.root.Join(withExecutablePlatformSuffix("gapis")), nil
}

func (l binLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.file(ctx, abi, abiToApk[abi.Architecture])
}

func (l binLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join(libTypeToName[lib]), nil
}

func (l binLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.root.Join(libTypeToJson[lib]), nil
}

func (l binLayout) GoArgs(ctx context.Context) []string {
	return nil
}

// runfilesLayout is a layout that uses the bazel generated runfiles manifest
// to find the various dependencies.
type runfilesLayout struct {
	manifest string
	mapping  map[string]string
}

var abiToApkPath = map[device.Architecture]string{
	device.ARMv7a: "armeabi-v7a.apk",
	device.ARMv8a: "arm64-v8a.apk",
	device.X86:    "x86.apk",
}

var libTypeToLibPath = map[LibraryType]string{
	LibGraphicsSpy:      withLibraryPlatformSuffix("gapid/gapii/cc/libgapii"),
	LibVirtualSwapChain: withLibraryPlatformSuffix("gapid/core/vulkan/vk_virtual_swapchain/cc/libVkLayer_VirtualSwapchain"),
}

var libTypeToJsonPath = map[LibraryType]string{
	LibGraphicsSpy:      "gapid/gapii/vulkan/vk_graphics_spy/cc/GraphicsSpyLayer.json",
	LibVirtualSwapChain: "gapid/core/vulkan/vk_virtual_swapchain/cc/VirtualSwapchainLayer.json",
}

// RunfilesLayout creates a new layout based on the given runfiles manifest.
func RunfilesLayout(manifest file.Path) (FileLayout, error) {
	file, err := os.Open(manifest.System())
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r := runfilesLayout{
		manifest: manifest.System(),
		mapping:  make(map[string]string),
	}
	for scanner.Scan() {
		line := scanner.Text()
		if p := strings.IndexRune(line, ' '); p > 0 {
			key, value := line[:p], line[p+1:]
			r.mapping[key] = value
		}
	}

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

func (l *runfilesLayout) find(key string) (file.Path, error) {
	if r, ok := l.mapping[key]; ok {
		return file.Abs(r), nil
	}
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l *runfilesLayout) Strings(ctx context.Context) (file.Path, error) {
	return l.find("gapid/gapis/messages/en-us.stb")
}

func (l *runfilesLayout) Gapit(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapit/gapit"))
}

func (l *runfilesLayout) Gapis(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapis/gapis"))
}

func (l *runfilesLayout) Gapir(ctx context.Context) (file.Path, error) {
	return l.find(withExecutablePlatformSuffix("gapid/cmd/gapir/cc/gapir"))
}

func (l *runfilesLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return l.find("gapid/gapidapk/android/apk/" + abiToApkPath[abi.Architecture])
}

func (l *runfilesLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.find(libTypeToLibPath[lib])
}

func (l *runfilesLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return l.find(libTypeToJsonPath[lib])
}

func (l *runfilesLayout) GoArgs(ctx context.Context) []string {
	return []string{"-runfiles", l.manifest}
}

// unknownLayout is the file layout used when no other layout can be discovered.
// All methods will return an error.
type unknownLayout struct{}

func (l unknownLayout) Strings(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapit(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapis(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Gapir(ctx context.Context) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) GapidApk(ctx context.Context, abi *device.ABI) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Library(ctx context.Context, lib LibraryType) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) Json(ctx context.Context, lib LibraryType) (file.Path, error) {
	return file.Path{}, ErrCannotFindPackageFiles
}

func (l unknownLayout) GoArgs(ctx context.Context) []string {
	return nil
}

// BinLayout returns a binLayout implementation of FileLayout rooted in the given directory.
func BinLayout(root file.Path) FileLayout {
	return binLayout{root}
}
<|endoftext|>


<commit_before>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

/*
	Helper functions
*/

// ObjectsAreEqual determines if two objects are considered equal.
//
// This function does no assertion of any kind.
func ObjectsAreEqual(a, b interface{}) bool {

	if reflect.DeepEqual(a, b) {
		return true
	}

	if reflect.ValueOf(a) == reflect.ValueOf(b) {
		return true
	}

	return false

}

/* CallerInfo is necessary because the assert functions use the testing object
internally, causing it to print the file:line of the assert method, rather than where
the problem actually occured in calling code.*/

// CallerInfo returns a string containing the file and line number of the assert call
// that failed.
func CallerInfo() string {
	_, file, line, ok := runtime.Caller(0)
	if !ok {
		return ""
	}
	parts := strings.Split(file, "/")
	thisDir := parts[len(parts)-2]

	for i := 1; ; i++ {
		_, file, line, ok = runtime.Caller(i)
		if !ok {
			return ""
		}
		parts = strings.Split(file, "/")
		dir := parts[len(parts)-2]
		file = parts[len(parts)-1]
		if thisDir != dir || file == "assertions_test.go" {
			break
		}
	}

	return fmt.Sprintf("[ %s:%d ] - ", file, line)
}

// Implements asserts that an object is implemented by the specified interface.
//
//    assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
func Implements(t *testing.T, interfaceObject interface{}, object interface{}, message ...string) bool {
	interfaceType := reflect.TypeOf(interfaceObject).Elem()
	return True(t, reflect.TypeOf(object).Implements(interfaceType), fmt.Sprintf("%sObject must implement %s. %s", CallerInfo(), interfaceType, message))
}

// IsType asserts that the specified objects are of the same type.
func IsType(t *testing.T, expectedType interface{}, object interface{}, message ...string) bool {
	return Equal(t, reflect.TypeOf(object), reflect.TypeOf(expectedType), fmt.Sprintf("Object expected to be of type %s, but was %s. %s", reflect.TypeOf(expectedType), reflect.TypeOf(object), message))
}

// Equal asserts that two objects are equal.
//
//    assert.Equal(t, 123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Equal(t *testing.T, a, b interface{}, message ...string) bool {

	if !ObjectsAreEqual(a, b) {
		t.Errorf("%s%s Not equal. %#v != %#v.", CallerInfo(), message, a, b)
		return false
	}
	return true

}

// NotNil asserts that the specified object is not nil.
//
//    assert.NotNil(t, err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t *testing.T, object interface{}, message ...string) bool {

	var success bool = true

	if object == nil {
		success = false
	} else if reflect.ValueOf(object).IsNil() {
		success = false
	}

	if !success {
		t.Errorf("%sExpected not to be nil. %s", CallerInfo(), message)
	}

	return success
}

// Nil asserts that the specified object is nil.
//
//    assert.Nil(t, err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func Nil(t *testing.T, object interface{}, message ...string) bool {

	if object == nil {
		return true
	} else if reflect.ValueOf(object).IsNil() {
		return true
	}

	t.Errorf("%sExpected to be nil but was %#v. %s", CallerInfo(), object, message)

	return false
}

// True asserts that the specified value is true.
//
//    assert.True(t, myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func True(t *testing.T, value bool, message ...string) bool {
	return Equal(t, true, value, message...)
}

// False asserts that the specified value is true.
//
//    assert.False(t, myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func False(t *testing.T, value bool, message ...string) bool {
	return Equal(t, false, value, message...)
}

// NotEqual asserts that the specified values are NOT equal.
//
//    assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func NotEqual(t *testing.T, a, b interface{}, message ...string) bool {

	if ObjectsAreEqual(a, b) {
		t.Errorf("%s%s Should not be equal. %#v == %#v.", CallerInfo(), message, a, b)
		return false
	}
	return true

}

// Contains asserts that the specified string contains the specified substring.
//
//    assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t *testing.T, s, contains string, message ...string) bool {

	if !strings.Contains(s, contains) {
		t.Errorf("%s %s '%s' does not contain '%s'", CallerInfo(), message, s, contains)
		return false
	}

	return true

}

// NotContains asserts that the specified string does NOT contain the specified substring.
//
//    assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t *testing.T, s, contains string, message ...string) bool {

	if strings.Contains(s, contains) {
		t.Errorf("%s%s '%s' should not contain '%s'", CallerInfo(), message, s, contains)
		return false
	}

	return true

}

// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
// methods, and represents a simple func that takes no arguments, and returns nothing.
type PanicTestFunc func()

// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) bool {

	var didPanic bool = false
	func() {

		defer func() {
			if r := recover(); r != nil {
				didPanic = true
			}
		}()

		// call the target function
		f()

	}()

	return didPanic

}

// Panics asserts that the code inside the specified PanicTestFunc panics.
//
//   assert.Panics(t, func(){
//     GoCrazy()
//   }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func Panics(t *testing.T, f PanicTestFunc, message ...string) bool {
	return True(t, didPanic(f), fmt.Sprintf("Func should panic but didn't. %s", message))
}

// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
//   assert.NotPanics(t, func(){
//     RemainCalm()
//   }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func NotPanics(t *testing.T, f PanicTestFunc, message ...string) bool {
	return False(t, didPanic(f), fmt.Sprintf("Func should not panic. %s", message))
}
<commit_msg>Fix mock CallerInfo<commit_after>package assert

import (
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

/*
	Helper functions
*/

// ObjectsAreEqual determines if two objects are considered equal.
//
// This function does no assertion of any kind.
func ObjectsAreEqual(a, b interface{}) bool {

	if reflect.DeepEqual(a, b) {
		return true
	}

	if reflect.ValueOf(a) == reflect.ValueOf(b) {
		return true
	}

	return false

}

/* CallerInfo is necessary because the assert functions use the testing object
internally, causing it to print the file:line of the assert method, rather than where
the problem actually occured in calling code.*/

// CallerInfo returns a string containing the file and line number of the assert call
// that failed.
func CallerInfo() string {

	file := ""
	line := 0
	ok := false

	for i := 0; ; i++ {
		_, file, line, ok = runtime.Caller(i)
		if !ok {
			return ""
		}
		parts := strings.Split(file, "/")
		dir := parts[len(parts)-2]
		file = parts[len(parts)-1]
		if (dir != "assert" && dir != "mock") || file == "mock_test.go" {
			break
		}
	}

	return fmt.Sprintf("[ %s:%d ] - ", file, line)
}

// Implements asserts that an object is implemented by the specified interface.
//
//    assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
func Implements(t *testing.T, interfaceObject interface{}, object interface{}, message ...string) bool {
	interfaceType := reflect.TypeOf(interfaceObject).Elem()
	return True(t, reflect.TypeOf(object).Implements(interfaceType), fmt.Sprintf("%sObject must implement %s. %s", CallerInfo(), interfaceType, message))
}

// IsType asserts that the specified objects are of the same type.
func IsType(t *testing.T, expectedType interface{}, object interface{}, message ...string) bool {
	return Equal(t, reflect.TypeOf(object), reflect.TypeOf(expectedType), fmt.Sprintf("Object expected to be of type %s, but was %s. %s", reflect.TypeOf(expectedType), reflect.TypeOf(object), message))
}

// Equal asserts that two objects are equal.
//
//    assert.Equal(t, 123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Equal(t *testing.T, a, b interface{}, message ...string) bool {

	if !ObjectsAreEqual(a, b) {
		t.Errorf("%s%s Not equal. %#v != %#v.", CallerInfo(), message, a, b)
		return false
	}
	return true

}

// NotNil asserts that the specified object is not nil.
//
//    assert.NotNil(t, err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t *testing.T, object interface{}, message ...string) bool {

	var success bool = true

	if object == nil {
		success = false
	} else if reflect.ValueOf(object).IsNil() {
		success = false
	}

	if !success {
		t.Errorf("%sExpected not to be nil. %s", CallerInfo(), message)
	}

	return success
}

// Nil asserts that the specified object is nil.
//
//    assert.Nil(t, err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func Nil(t *testing.T, object interface{}, message ...string) bool {

	if object == nil {
		return true
	} else if reflect.ValueOf(object).IsNil() {
		return true
	}

	t.Errorf("%sExpected to be nil but was %#v. %s", CallerInfo(), object, message)

	return false
}

// True asserts that the specified value is true.
//
//    assert.True(t, myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func True(t *testing.T, value bool, message ...string) bool {
	return Equal(t, true, value, message...)
}

// False asserts that the specified value is true.
//
//    assert.False(t, myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func False(t *testing.T, value bool, message ...string) bool {
	return Equal(t, false, value, message...)
}

// NotEqual asserts that the specified values are NOT equal.
//
//    assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func NotEqual(t *testing.T, a, b interface{}, message ...string) bool {

	if ObjectsAreEqual(a, b) {
		t.Errorf("%s%s Should not be equal. %#v == %#v.", CallerInfo(), message, a, b)
		return false
	}
	return true

}

// Contains asserts that the specified string contains the specified substring.
//
//    assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t *testing.T, s, contains string, message ...string) bool {

	if !strings.Contains(s, contains) {
		t.Errorf("%s %s '%s' does not contain '%s'", CallerInfo(), message, s, contains)
		return false
	}

	return true

}

// NotContains asserts that the specified string does NOT contain the specified substring.
//
//    assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t *testing.T, s, contains string, message ...string) bool {

	if strings.Contains(s, contains) {
		t.Errorf("%s%s '%s' should not contain '%s'", CallerInfo(), message, s, contains)
		return false
	}

	return true

}

// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
// methods, and represents a simple func that takes no arguments, and returns nothing.
type PanicTestFunc func()

// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) bool {

	var didPanic bool = false
	func() {

		defer func() {
			if r := recover(); r != nil {
				didPanic = true
			}
		}()

		// call the target function
		f()

	}()

	return didPanic

}

// Panics asserts that the code inside the specified PanicTestFunc panics.
//
//   assert.Panics(t, func(){
//     GoCrazy()
//   }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func Panics(t *testing.T, f PanicTestFunc, message ...string) bool {
	return True(t, didPanic(f), fmt.Sprintf("Func should panic but didn't. %s", message))
}

// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
//   assert.NotPanics(t, func(){
//     RemainCalm()
//   }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func NotPanics(t *testing.T, f PanicTestFunc, message ...string) bool {
	return False(t, didPanic(f), fmt.Sprintf("Func should not panic. %s", message))
}
<|endoftext|>