<commit_before>package conf_test

import (
	"encoding/json"
	"testing"

	"github.com/golang/protobuf/proto"

	"v2ray.com/core"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/app/proxyman"
	"v2ray.com/core/app/router"
	clog "v2ray.com/core/common/log"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/serial"
	. "v2ray.com/core/infra/conf"
	"v2ray.com/core/proxy/blackhole"
	dns_proxy "v2ray.com/core/proxy/dns"
	"v2ray.com/core/proxy/freedom"
	"v2ray.com/core/proxy/vmess"
	"v2ray.com/core/proxy/vmess/inbound"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/http"
	"v2ray.com/core/transport/internet/tls"
	"v2ray.com/core/transport/internet/websocket"
)

func TestV2RayConfig(t *testing.T) {
	createParser := func() func(string) (proto.Message, error) {
		return func(s string) (proto.Message, error) {
			config := new(Config)
			if err := json.Unmarshal([]byte(s), config); err != nil {
				return nil, err
			}
			return config.Build()
		}
	}

	runMultiTestCase(t, []TestCase{
		{
			Input: `{
				"outbound": {
					"protocol": "freedom",
					"settings": {}
				},
				"log": {
					"access": "/var/log/v2ray/access.log",
					"loglevel": "error",
					"error": "/var/log/v2ray/error.log"
				},
				"inbound": {
					"streamSettings": {
						"network": "ws",
						"wsSettings": {
							"headers": {
								"host": "example.domain"
							},
							"path": ""
						},
						"tlsSettings": {
							"alpn": "h2"
						},
						"security": "tls"
					},
					"protocol": "vmess",
					"port": 443,
					"settings": {
						"clients": [
							{
								"alterId": 100,
								"security": "aes-128-gcm",
								"id": "0cdf8a45-303d-4fed-9780-29aa7f54175e"
							}
						]
					}
				},
				"inbounds": [{
					"streamSettings": {
						"network": "ws",
						"wsSettings": {
							"headers": {
								"host": "example.domain"
							},
							"path": ""
						},
						"tlsSettings": {
							"alpn": "h2"
						},
						"security": "tls"
					},
					"protocol": "vmess",
					"port": "443-500",
					"allocate": {
						"strategy": "random",
						"concurrency": 3
					},
					"settings": {
						"clients": [
							{
								"alterId": 100,
								"security": "aes-128-gcm",
								"id": "0cdf8a45-303d-4fed-9780-29aa7f54175e"
							}
						]
					}
				}],
				"outboundDetour": [
					{
						"tag": "blocked",
						"protocol": "blackhole"
					},
					{
						"protocol": "dns"
					}
				],
				"routing": {
					"strategy": "rules",
					"settings": {
						"rules": [
							{
								"ip": [
									"10.0.0.0/8"
								],
								"type": "field",
								"outboundTag": "blocked"
							}
						]
					}
				},
				"transport": {
					"httpSettings": {
						"path": "/test"
					}
				}
			}`,
			Parser: createParser(),
			Output: &core.Config{
				App: []*serial.TypedMessage{
					serial.ToTypedMessage(&dispatcher.Config{}),
					serial.ToTypedMessage(&proxyman.InboundConfig{}),
					serial.ToTypedMessage(&proxyman.OutboundConfig{}),
					serial.ToTypedMessage(&log.Config{
						ErrorLogType:  log.LogType_File,
						ErrorLogPath:  "/var/log/v2ray/error.log",
						ErrorLogLevel: clog.Severity_Error,
						AccessLogType: log.LogType_File,
						AccessLogPath: "/var/log/v2ray/access.log",
					}),
					serial.ToTypedMessage(&router.Config{
						DomainStrategy: router.Config_AsIs,
						Rule: []*router.RoutingRule{
							{
								Geoip: []*router.GeoIP{
									{
										Cidr: []*router.CIDR{
											{
												Ip:     []byte{10, 0, 0, 0},
												Prefix: 8,
											},
										},
									},
								},
								TargetTag: &router.RoutingRule_Tag{
									Tag: "blocked",
								},
							},
						},
					}),
				},
				Outbound: []*core.OutboundHandlerConfig{
					{
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&freedom.Config{
							DomainStrategy: freedom.Config_AS_IS,
							UserLevel:      0,
						}),
					},
					{
						Tag: "blocked",
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&blackhole.Config{}),
					},
					{
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
							Server: &net.Endpoint{},
						}),
					},
				},
				Inbound: []*core.InboundHandlerConfig{
					{
						ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
							PortRange: &net.PortRange{
								From: 443,
								To:   443,
							},
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "websocket",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "websocket",
										Settings: serial.ToTypedMessage(&websocket.Config{
											Header: []*websocket.Header{
												{
													Key:   "host",
													Value: "example.domain",
												},
											},
										}),
									},
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
								SecurityType: "v2ray.core.transport.internet.tls.Config",
								SecuritySettings: []*serial.TypedMessage{
									serial.ToTypedMessage(&tls.Config{
										NextProtocol: []string{"h2"},
									}),
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&inbound.Config{
							User: []*protocol.User{
								{
									Level: 0,
									Account: serial.ToTypedMessage(&vmess.Account{
										Id:      "0cdf8a45-303d-4fed-9780-29aa7f54175e",
										AlterId: 100,
										SecuritySettings: &protocol.SecurityConfig{
											Type: protocol.SecurityType_AES128_GCM,
										},
									}),
								},
							},
						}),
					},
					{
						ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
							PortRange: &net.PortRange{
								From: 443,
								To:   500,
							},
							AllocationStrategy: &proxyman.AllocationStrategy{
								Type: proxyman.AllocationStrategy_Random,
								Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
									Value: 3,
								},
							},
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "websocket",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "websocket",
										Settings: serial.ToTypedMessage(&websocket.Config{
											Header: []*websocket.Header{
												{
													Key:   "host",
													Value: "example.domain",
												},
											},
										}),
									},
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
								SecurityType: "v2ray.core.transport.internet.tls.Config",
								SecuritySettings: []*serial.TypedMessage{
									serial.ToTypedMessage(&tls.Config{
										NextProtocol: []string{"h2"},
									}),
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&inbound.Config{
							User: []*protocol.User{
								{
									Level: 0,
									Account: serial.ToTypedMessage(&vmess.Account{
										Id:      "0cdf8a45-303d-4fed-9780-29aa7f54175e",
										AlterId: 100,
										SecuritySettings: &protocol.SecurityConfig{
											Type: protocol.SecurityType_AES128_GCM,
										},
									}),
								},
							},
						}),
					},
				},
			},
		},
	})
}
<commit_msg>fix test failed on logger position<commit_after>package conf_test

import (
	"encoding/json"
	"testing"

	"github.com/golang/protobuf/proto"

	"v2ray.com/core"
	"v2ray.com/core/app/dispatcher"
	"v2ray.com/core/app/log"
	"v2ray.com/core/app/proxyman"
	"v2ray.com/core/app/router"
	clog "v2ray.com/core/common/log"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/serial"
	. "v2ray.com/core/infra/conf"
	"v2ray.com/core/proxy/blackhole"
	dns_proxy "v2ray.com/core/proxy/dns"
	"v2ray.com/core/proxy/freedom"
	"v2ray.com/core/proxy/vmess"
	"v2ray.com/core/proxy/vmess/inbound"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/http"
	"v2ray.com/core/transport/internet/tls"
	"v2ray.com/core/transport/internet/websocket"
)

func TestV2RayConfig(t *testing.T) {
	createParser := func() func(string) (proto.Message, error) {
		return func(s string) (proto.Message, error) {
			config := new(Config)
			if err := json.Unmarshal([]byte(s), config); err != nil {
				return nil, err
			}
			return config.Build()
		}
	}

	runMultiTestCase(t, []TestCase{
		{
			Input: `{
				"outbound": {
					"protocol": "freedom",
					"settings": {}
				},
				"log": {
					"access": "/var/log/v2ray/access.log",
					"loglevel": "error",
					"error": "/var/log/v2ray/error.log"
				},
				"inbound": {
					"streamSettings": {
						"network": "ws",
						"wsSettings": {
							"headers": {
								"host": "example.domain"
							},
							"path": ""
						},
						"tlsSettings": {
							"alpn": "h2"
						},
						"security": "tls"
					},
					"protocol": "vmess",
					"port": 443,
					"settings": {
						"clients": [
							{
								"alterId": 100,
								"security": "aes-128-gcm",
								"id": "0cdf8a45-303d-4fed-9780-29aa7f54175e"
							}
						]
					}
				},
				"inbounds": [{
					"streamSettings": {
						"network": "ws",
						"wsSettings": {
							"headers": {
								"host": "example.domain"
							},
							"path": ""
						},
						"tlsSettings": {
							"alpn": "h2"
						},
						"security": "tls"
					},
					"protocol": "vmess",
					"port": "443-500",
					"allocate": {
						"strategy": "random",
						"concurrency": 3
					},
					"settings": {
						"clients": [
							{
								"alterId": 100,
								"security": "aes-128-gcm",
								"id": "0cdf8a45-303d-4fed-9780-29aa7f54175e"
							}
						]
					}
				}],
				"outboundDetour": [
					{
						"tag": "blocked",
						"protocol": "blackhole"
					},
					{
						"protocol": "dns"
					}
				],
				"routing": {
					"strategy": "rules",
					"settings": {
						"rules": [
							{
								"ip": [
									"10.0.0.0/8"
								],
								"type": "field",
								"outboundTag": "blocked"
							}
						]
					}
				},
				"transport": {
					"httpSettings": {
						"path": "/test"
					}
				}
			}`,
			Parser: createParser(),
			Output: &core.Config{
				App: []*serial.TypedMessage{
					serial.ToTypedMessage(&log.Config{
						ErrorLogType:  log.LogType_File,
						ErrorLogPath:  "/var/log/v2ray/error.log",
						ErrorLogLevel: clog.Severity_Error,
						AccessLogType: log.LogType_File,
						AccessLogPath: "/var/log/v2ray/access.log",
					}),
					serial.ToTypedMessage(&dispatcher.Config{}),
					serial.ToTypedMessage(&proxyman.InboundConfig{}),
					serial.ToTypedMessage(&proxyman.OutboundConfig{}),
					serial.ToTypedMessage(&router.Config{
						DomainStrategy: router.Config_AsIs,
						Rule: []*router.RoutingRule{
							{
								Geoip: []*router.GeoIP{
									{
										Cidr: []*router.CIDR{
											{
												Ip:     []byte{10, 0, 0, 0},
												Prefix: 8,
											},
										},
									},
								},
								TargetTag: &router.RoutingRule_Tag{
									Tag: "blocked",
								},
							},
						},
					}),
				},
				Outbound: []*core.OutboundHandlerConfig{
					{
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&freedom.Config{
							DomainStrategy: freedom.Config_AS_IS,
							UserLevel:      0,
						}),
					},
					{
						Tag: "blocked",
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&blackhole.Config{}),
					},
					{
						SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "tcp",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
							Server: &net.Endpoint{},
						}),
					},
				},
				Inbound: []*core.InboundHandlerConfig{
					{
						ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
							PortRange: &net.PortRange{
								From: 443,
								To:   443,
							},
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "websocket",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "websocket",
										Settings: serial.ToTypedMessage(&websocket.Config{
											Header: []*websocket.Header{
												{
													Key:   "host",
													Value: "example.domain",
												},
											},
										}),
									},
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
								SecurityType: "v2ray.core.transport.internet.tls.Config",
								SecuritySettings: []*serial.TypedMessage{
									serial.ToTypedMessage(&tls.Config{
										NextProtocol: []string{"h2"},
									}),
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&inbound.Config{
							User: []*protocol.User{
								{
									Level: 0,
									Account: serial.ToTypedMessage(&vmess.Account{
										Id:      "0cdf8a45-303d-4fed-9780-29aa7f54175e",
										AlterId: 100,
										SecuritySettings: &protocol.SecurityConfig{
											Type: protocol.SecurityType_AES128_GCM,
										},
									}),
								},
							},
						}),
					},
					{
						ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
							PortRange: &net.PortRange{
								From: 443,
								To:   500,
							},
							AllocationStrategy: &proxyman.AllocationStrategy{
								Type: proxyman.AllocationStrategy_Random,
								Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
									Value: 3,
								},
							},
							StreamSettings: &internet.StreamConfig{
								ProtocolName: "websocket",
								TransportSettings: []*internet.TransportConfig{
									{
										ProtocolName: "websocket",
										Settings: serial.ToTypedMessage(&websocket.Config{
											Header: []*websocket.Header{
												{
													Key:   "host",
													Value: "example.domain",
												},
											},
										}),
									},
									{
										ProtocolName: "http",
										Settings: serial.ToTypedMessage(&http.Config{
											Path: "/test",
										}),
									},
								},
								SecurityType: "v2ray.core.transport.internet.tls.Config",
								SecuritySettings: []*serial.TypedMessage{
									serial.ToTypedMessage(&tls.Config{
										NextProtocol: []string{"h2"},
									}),
								},
							},
						}),
						ProxySettings: serial.ToTypedMessage(&inbound.Config{
							User: []*protocol.User{
								{
									Level: 0,
									Account: serial.ToTypedMessage(&vmess.Account{
										Id:      "0cdf8a45-303d-4fed-9780-29aa7f54175e",
										AlterId: 100,
										SecuritySettings: &protocol.SecurityConfig{
											Type: protocol.SecurityType_AES128_GCM,
										},
									}),
								},
							},
						}),
					},
				},
			},
		},
	})
}
<|endoftext|>


<commit_before>package integer_sample

import (
	"fmt"
	"math/rand"
)

type IntegerSample struct {
	Sample []string
	Size int
	candidate int
	count int
}

// Print prints the values of the sample to STDOUT.
func (sample *IntegerSample) Print() {
	for _, line := range sample.Sample {
		fmt.Println(line)
	}
}

// SampleLine performs a classic 'resevoir sample'.
// Choose a number, N, between 0 and , if N is >= sample.Size
// we return; if N is < sample.Size we replace it with `line` at
// the Nth index of sample.Sample.
func (sample *IntegerSample) SampleLine(line string) {

	if sample.count < sample.Size {
		goto ADD_SAMPLE
	}

	sample.candidate = rand.Intn(sample.count)
	if sample.candidate < sample.Size {
		sample.Sample[sample.candidate] = line
		goto INCREMENT_COUNT
	}

	return

ADD_SAMPLE:
	sample.Sample[sample.count] = line
	goto INCREMENT_COUNT

INCREMENT_COUNT:
	sample.count++

	return
}
<commit_msg>big space<commit_after>package integer_sample

import (
	"fmt"
	"math/rand"
)

type IntegerSample struct {
	Sample []string
	Size int
	candidate int
	count int
}

// Print prints the values of the sample to STDOUT.
func (sample *IntegerSample) Print() {
	for _, line := range sample.Sample {
		fmt.Println(line)
	}
}

// SampleLine performs a classic 'resevoir sample'.
// Choose a number, N, between 0 and , if N is >= sample.Size
// we return; if N is < sample.Size we replace it with `line` at
// the Nth index of sample.Sample.
func (sample *IntegerSample) SampleLine(line string) {

	if sample.count < sample.Size {
		goto ADD_SAMPLE
	}

	sample.candidate = rand.Intn(sample.count)
	if sample.candidate < sample.Size {
		sample.Sample[sample.candidate] = line
		goto INCREMENT_COUNT
	}

	return

ADD_SAMPLE:
	sample.Sample[sample.count] = line
	goto INCREMENT_COUNT

INCREMENT_COUNT:
	sample.count++

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 integration

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
	"github.com/GoogleContainerTools/skaffold/proto"
)

func TestDevSync(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	tests := []struct {
		description string
		trigger     string
		config      string
	}{
		{
			description: "manual sync with polling trigger",
			trigger:     "polling",
			config:      "skaffold-manual.yaml",
		},
		{
			description: "manual sync with notify trigger",
			trigger:     "notify",
			config:      "skaffold-manual.yaml",
		},
		{
			description: "inferred sync with notify trigger",
			trigger:     "notify",
			config:      "skaffold-infer.yaml",
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			// Run skaffold build first to fail quickly on a build failure
			skaffold.Build().InDir("testdata/file-sync").WithConfig(test.config).RunOrFail(t)

			ns, client, deleteNs := SetupNamespace(t)
			defer deleteNs()

			stop := skaffold.Dev("--trigger", test.trigger).InDir("testdata/file-sync").WithConfig(test.config).InNs(ns.Name).RunBackground(t)
			defer stop()

			client.WaitForPodsReady("test-file-sync")

			ioutil.WriteFile("testdata/file-sync/foo", []byte("foo"), 0644)
			defer func() { os.Truncate("testdata/file-sync/foo", 0) }()

			err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", "foo").Output()
				return string(out) == "foo", nil
			})
			failNowIfError(t, err)
		})
	}
}

func TestDevAutoSync(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	dir := "testdata/jib-sync/"

	tests := []struct {
		description string
		profiles    []string
		uniqueStr   string
	}{
		{
			description: "jib maven auto sync",
			profiles:    []string{"maven"},
			uniqueStr:   "maven-maven",
		},
		{
			description: "jib gradle auto sync",
			profiles:    []string{"gradle"},
			uniqueStr:   "gradle-gradle",
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			// Run skaffold build first to fail quickly on a build failure
			skaffold.Build().WithProfiles(test.profiles).InDir(dir).RunOrFail(t)

			ns, client, deleteNs := SetupNamespace(t)
			defer deleteNs()

			_, cancel := skaffold.Dev("--trigger", "notify").WithProfiles(test.profiles).InDir(dir).InNs(ns.Name).RunBackgroundOutput(t)
			defer cancel()

			client.WaitForPodsReady("test-file-sync")
			// give the server a chance to warm up, this integration test on KIND has a tendency to fail if
			// this doesn't happen, I can't recreate this in any other environment, it some very specific
			// server startup race condition that occurs in super slow environments (ex: KIND on travis).
			time.Sleep(time.Second * 5)

			// direct file sync (this file is an existing file checked in for this testdata)
			directFile := "direct-file"
			directFilePath := dir + "src/main/jib/" + directFile
			directFileData := "direct-data"
			if err := ioutil.WriteFile(directFilePath, []byte(directFileData), 0644); err != nil {
				t.Fatalf("Failed to write local file to sync %s", directFilePath)
			}
			defer func() { os.Truncate(directFilePath, 0) }()

			err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", directFile).Output()
				return string(out) == directFileData, nil
			})
			failNowIfError(t, err)

			// compile and sync
			generatedFileSrc := dir + "src/main/java/hello/HelloController.java"
			if oldContents, err := ioutil.ReadFile(generatedFileSrc); err != nil {
				t.Fatalf("Failed to read file %s", generatedFileSrc)
			} else {
				newContents := strings.Replace(string(oldContents), "text-to-replace", test.uniqueStr, 1)
				if err := ioutil.WriteFile(generatedFileSrc, []byte(newContents), 0644); err != nil {
					t.Fatalf("Failed to write new contents to file %s", generatedFileSrc)
				}
				defer func() {
					ioutil.WriteFile(generatedFileSrc, oldContents, 0644)
				}()
			}
			err = wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				// distroless debug only has wget, not curl
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "wget", "localhost:8080/", "-q", "-O", "-").Output()
				return string(out) == test.uniqueStr, nil
			})
			failNowIfError(t, err)
		})
	}
}

func TestDevSyncAPITrigger(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	ns, client, deleteNs := SetupNamespace(t)
	defer deleteNs()

	skaffold.Build().InDir("testdata/file-sync").WithConfig("skaffold-manual.yaml").InNs(ns.Name).RunOrFail(t)

	rpcAddr := randomPort()

	stop := skaffold.Dev("--auto-sync=false", "--rpc-port", rpcAddr).InDir("testdata/file-sync").WithConfig("skaffold-manual.yaml").InNs(ns.Name).RunBackground(t)
	defer stop()

	rpcClient, entries, shutdown := apiEvents(t, rpcAddr)
	defer shutdown()

	// throw away first 5 entries of log (from first run of dev loop)
	for i := 0; i < 5; i++ {
		<-entries
	}

	client.WaitForPodsReady("test-file-sync")

	ioutil.WriteFile("testdata/file-sync/foo", []byte("foo"), 0644)
	defer func() { os.Truncate("testdata/file-sync/foo", 0) }()

	rpcClient.Execute(context.Background(), &proto.UserIntentRequest{
		Intent: &proto.Intent{
			Sync: true,
		},
	})

	// Ensure we see a file sync in progress triggered in the event log
	err := wait.PollImmediate(time.Millisecond*500, 2*time.Minute, func() (bool, error) {
		e := <-entries
		return e.GetEvent().GetFileSyncEvent().GetStatus() == "In Progress", nil
	})
	failNowIfError(t, err)

	err = wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
		out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", "foo").Output()
		return string(out) == "foo", nil
	})
	failNowIfError(t, err)

	// Ensure we see a file sync succeeded triggered in the event log
	err = wait.PollImmediate(time.Millisecond*500, 2*time.Minute, func() (bool, error) {
		e := <-entries
		return e.GetEvent().GetFileSyncEvent().GetStatus() == "Succeeded", nil
	})
	failNowIfError(t, err)
}
<commit_msg>maybe 5 wasn't enough<commit_after>/*
Copyright 2019 The Skaffold 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 integration

import (
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
	"github.com/GoogleContainerTools/skaffold/proto"
)

func TestDevSync(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	tests := []struct {
		description string
		trigger     string
		config      string
	}{
		{
			description: "manual sync with polling trigger",
			trigger:     "polling",
			config:      "skaffold-manual.yaml",
		},
		{
			description: "manual sync with notify trigger",
			trigger:     "notify",
			config:      "skaffold-manual.yaml",
		},
		{
			description: "inferred sync with notify trigger",
			trigger:     "notify",
			config:      "skaffold-infer.yaml",
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			// Run skaffold build first to fail quickly on a build failure
			skaffold.Build().InDir("testdata/file-sync").WithConfig(test.config).RunOrFail(t)

			ns, client, deleteNs := SetupNamespace(t)
			defer deleteNs()

			stop := skaffold.Dev("--trigger", test.trigger).InDir("testdata/file-sync").WithConfig(test.config).InNs(ns.Name).RunBackground(t)
			defer stop()

			client.WaitForPodsReady("test-file-sync")

			ioutil.WriteFile("testdata/file-sync/foo", []byte("foo"), 0644)
			defer func() { os.Truncate("testdata/file-sync/foo", 0) }()

			err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", "foo").Output()
				return string(out) == "foo", nil
			})
			failNowIfError(t, err)
		})
	}
}

func TestDevAutoSync(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	dir := "testdata/jib-sync/"

	tests := []struct {
		description string
		profiles    []string
		uniqueStr   string
	}{
		{
			description: "jib maven auto sync",
			profiles:    []string{"maven"},
			uniqueStr:   "maven-maven",
		},
		{
			description: "jib gradle auto sync",
			profiles:    []string{"gradle"},
			uniqueStr:   "gradle-gradle",
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			// Run skaffold build first to fail quickly on a build failure
			skaffold.Build().WithProfiles(test.profiles).InDir(dir).RunOrFail(t)

			ns, client, deleteNs := SetupNamespace(t)
			defer deleteNs()

			_, cancel := skaffold.Dev("--trigger", "notify").WithProfiles(test.profiles).InDir(dir).InNs(ns.Name).RunBackgroundOutput(t)
			defer cancel()

			client.WaitForPodsReady("test-file-sync")
			// give the server a chance to warm up, this integration test on KIND has a tendency to fail if
			// this doesn't happen, I can't recreate this in any other environment, it some very specific
			// server startup race condition that occurs in super slow environments (ex: KIND on travis).
			// TODO: change this to watch the input, and go after the server is started
			time.Sleep(time.Second * 15)

			// direct file sync (this file is an existing file checked in for this testdata)
			directFile := "direct-file"
			directFilePath := dir + "src/main/jib/" + directFile
			directFileData := "direct-data"
			if err := ioutil.WriteFile(directFilePath, []byte(directFileData), 0644); err != nil {
				t.Fatalf("Failed to write local file to sync %s", directFilePath)
			}
			defer func() { os.Truncate(directFilePath, 0) }()

			err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", directFile).Output()
				return string(out) == directFileData, nil
			})
			failNowIfError(t, err)

			// compile and sync
			generatedFileSrc := dir + "src/main/java/hello/HelloController.java"
			if oldContents, err := ioutil.ReadFile(generatedFileSrc); err != nil {
				t.Fatalf("Failed to read file %s", generatedFileSrc)
			} else {
				newContents := strings.Replace(string(oldContents), "text-to-replace", test.uniqueStr, 1)
				if err := ioutil.WriteFile(generatedFileSrc, []byte(newContents), 0644); err != nil {
					t.Fatalf("Failed to write new contents to file %s", generatedFileSrc)
				}
				defer func() {
					ioutil.WriteFile(generatedFileSrc, oldContents, 0644)
				}()
			}
			err = wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
				// distroless debug only has wget, not curl
				out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "wget", "localhost:8080/", "-q", "-O", "-").Output()
				return string(out) == test.uniqueStr, nil
			})
			failNowIfError(t, err)
		})
	}
}

func TestDevSyncAPITrigger(t *testing.T) {
	if testing.Short() || RunOnGCP() {
		t.Skip("skipping kind integration test")
	}

	ns, client, deleteNs := SetupNamespace(t)
	defer deleteNs()

	skaffold.Build().InDir("testdata/file-sync").WithConfig("skaffold-manual.yaml").InNs(ns.Name).RunOrFail(t)

	rpcAddr := randomPort()

	stop := skaffold.Dev("--auto-sync=false", "--rpc-port", rpcAddr).InDir("testdata/file-sync").WithConfig("skaffold-manual.yaml").InNs(ns.Name).RunBackground(t)
	defer stop()

	rpcClient, entries, shutdown := apiEvents(t, rpcAddr)
	defer shutdown()

	// throw away first 5 entries of log (from first run of dev loop)
	for i := 0; i < 5; i++ {
		<-entries
	}

	client.WaitForPodsReady("test-file-sync")

	ioutil.WriteFile("testdata/file-sync/foo", []byte("foo"), 0644)
	defer func() { os.Truncate("testdata/file-sync/foo", 0) }()

	rpcClient.Execute(context.Background(), &proto.UserIntentRequest{
		Intent: &proto.Intent{
			Sync: true,
		},
	})

	// Ensure we see a file sync in progress triggered in the event log
	err := wait.PollImmediate(time.Millisecond*500, 2*time.Minute, func() (bool, error) {
		e := <-entries
		return e.GetEvent().GetFileSyncEvent().GetStatus() == "In Progress", nil
	})
	failNowIfError(t, err)

	err = wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
		out, _ := exec.Command("kubectl", "exec", "test-file-sync", "-n", ns.Name, "--", "cat", "foo").Output()
		return string(out) == "foo", nil
	})
	failNowIfError(t, err)

	// Ensure we see a file sync succeeded triggered in the event log
	err = wait.PollImmediate(time.Millisecond*500, 2*time.Minute, func() (bool, error) {
		e := <-entries
		return e.GetEvent().GetFileSyncEvent().GetStatus() == "Succeeded", nil
	})
	failNowIfError(t, err)
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package proxy provides a client for interacting with a proxy.
package proxy

import (
	"archive/zip"
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"go.opencensus.io/plugin/ochttp"
	"golang.org/x/mod/module"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
)

// A Client is used by the fetch service to communicate with a module
// proxy. It handles all methods defined by go help goproxy.
type Client struct {
	// URL of the module proxy web server
	url string

	// client used for HTTP requests. It is mutable for testing purposes.
	httpClient *http.Client
}

// A VersionInfo contains metadata about a given version of a module.
type VersionInfo struct {
	Version string
	Time    time.Time
}

// Setting this header to true prevents the proxy from fetching uncached
// modules.
const disableFetchHeader = "Disable-Module-Fetch"

// New constructs a *Client using the provided url, which is expected to
// be an absolute URI that can be directly passed to http.Get.
func New(u string) (_ *Client, err error) {
	defer derrors.Wrap(&err, "proxy.New(%q)", u)
	return &Client{
		url:        strings.TrimRight(u, "/"),
		httpClient: &http.Client{Transport: &ochttp.Transport{}},
	}, nil
}

// GetInfo makes a request to $GOPROXY/<module>/@v/<requestedVersion>.info and
// transforms that data into a *VersionInfo.
func (c *Client) GetInfo(ctx context.Context, modulePath, requestedVersion string) (_ *VersionInfo, err error) {
	return c.getInfo(ctx, modulePath, requestedVersion, false)
}

// GetInfoNoFetch behaves like GetInfo, except that it sets the
// Disable-Module-Fetch header so that the proxy does not fetch a module it
// doesn't already know about.
func (c *Client) GetInfoNoFetch(ctx context.Context, modulePath, requestedVersion string) (_ *VersionInfo, err error) {
	return c.getInfo(ctx, modulePath, requestedVersion, true)
}

func (c *Client) getInfo(ctx context.Context, modulePath, requestedVersion string, disableFetch bool) (_ *VersionInfo, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetInfo(%q, %q)", modulePath, requestedVersion)
	data, err := c.readBody(ctx, modulePath, requestedVersion, "info", disableFetch)
	if err != nil {
		return nil, err
	}
	var v VersionInfo
	if err := json.Unmarshal(data, &v); err != nil {
		return nil, err
	}
	return &v, nil
}

// GetMod makes a request to $GOPROXY/<module>/@v/<resolvedVersion>.mod and returns the raw data.
func (c *Client) GetMod(ctx context.Context, modulePath, resolvedVersion string) (_ []byte, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetMod(%q, %q)", modulePath, resolvedVersion)
	return c.readBody(ctx, modulePath, resolvedVersion, "mod", false)
}

// GetZip makes a request to $GOPROXY/<modulePath>/@v/<resolvedVersion>.zip and
// transforms that data into a *zip.Reader. <resolvedVersion> must have already
// been resolved by first making a request to
// $GOPROXY/<modulePath>/@v/<requestedVersion>.info to obtained the valid
// semantic version.
func (c *Client) GetZip(ctx context.Context, modulePath, resolvedVersion string) (_ *zip.Reader, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetZip(ctx, %q, %q)", modulePath, resolvedVersion)

	bodyBytes, err := c.readBody(ctx, modulePath, resolvedVersion, "zip", false)
	if err != nil {
		return nil, err
	}
	zipReader, err := zip.NewReader(bytes.NewReader(bodyBytes), int64(len(bodyBytes)))
	if err != nil {
		return nil, fmt.Errorf("zip.NewReader: %v: %w", err, derrors.BadModule)
	}
	return zipReader, nil
}

// GetZipSize gets the size in bytes of the zip from the proxy, without downloading it.
// The version must be resolved, as by a call to Client.GetInfo.
func (c *Client) GetZipSize(ctx context.Context, modulePath, resolvedVersion string) (_ int64, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetZipSize(ctx, %q, %q)", modulePath, resolvedVersion)

	url, err := c.escapedURL(modulePath, resolvedVersion, "zip")
	if err != nil {
		return 0, err
	}
	res, err := ctxhttp.Head(ctx, c.httpClient, url)
	if err != nil {
		return 0, fmt.Errorf("ctxhttp.Head(ctx, client, %q): %v", url, err)
	}
	defer res.Body.Close()
	if err := responseError(res, false); err != nil {
		return 0, err
	}
	if res.ContentLength < 0 {
		return 0, errors.New("unknown content length")
	}
	return res.ContentLength, nil
}

func (c *Client) escapedURL(modulePath, requestedVersion, suffix string) (_ string, err error) {
	defer func() {
		derrors.Wrap(&err, "Client.escapedURL(%q, %q, %q)", modulePath, requestedVersion, suffix)
	}()

	if suffix != "info" && suffix != "mod" && suffix != "zip" {
		return "", errors.New(`suffix must be "info", "mod" or "zip"`)
	}
	escapedPath, err := module.EscapePath(modulePath)
	if err != nil {
		return "", fmt.Errorf("path: %v: %w", err, derrors.InvalidArgument)
	}
	if requestedVersion == internal.LatestVersion {
		if suffix != "info" {
			return "", fmt.Errorf("cannot ask for latest with suffix %q", suffix)
		}
		return fmt.Sprintf("%s/%s/@latest", c.url, escapedPath), nil
	}
	escapedVersion, err := module.EscapeVersion(requestedVersion)
	if err != nil {
		return "", fmt.Errorf("version: %v: %w", err, derrors.InvalidArgument)
	}
	return fmt.Sprintf("%s/%s/@v/%s.%s", c.url, escapedPath, escapedVersion, suffix), nil
}

func (c *Client) readBody(ctx context.Context, modulePath, requestedVersion, suffix string, disableFetch bool) (_ []byte, err error) {
	defer derrors.Wrap(&err, "Client.readBody(%q, %q, %q)", modulePath, requestedVersion, suffix)

	u, err := c.escapedURL(modulePath, requestedVersion, suffix)
	if err != nil {
		return nil, err
	}
	var data []byte
	err = c.executeRequest(ctx, u, disableFetch, func(body io.Reader) error {
		var err error
		data, err = ioutil.ReadAll(body)
		return err
	})
	if err != nil {
		return nil, err
	}
	return data, nil
}

// ListVersions makes a request to $GOPROXY/<path>/@v/list and returns the
// resulting version strings.
func (c *Client) ListVersions(ctx context.Context, modulePath string) ([]string, error) {
	escapedPath, err := module.EscapePath(modulePath)
	if err != nil {
		return nil, fmt.Errorf("module.EscapePath(%q): %w", modulePath, derrors.InvalidArgument)
	}
	u := fmt.Sprintf("%s/%s/@v/list", c.url, escapedPath)
	var versions []string
	collect := func(body io.Reader) error {
		scanner := bufio.NewScanner(body)
		for scanner.Scan() {
			versions = append(versions, scanner.Text())
		}
		return scanner.Err()
	}
	if err := c.executeRequest(ctx, u, false, collect); err != nil {
		return nil, err
	}
	return versions, nil
}

// executeRequest executes an HTTP GET request for u, then calls the bodyFunc
// on the response body, if no error occurred.
func (c *Client) executeRequest(ctx context.Context, u string, disableFetch bool, bodyFunc func(body io.Reader) error) (err error) {
	defer func() {
		if ctx.Err() != nil {
			err = fmt.Errorf("%v: %w", err, derrors.ProxyTimedOut)
		}
		derrors.Wrap(&err, "executeRequest(ctx, %q)", u)
	}()

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	if disableFetch {
		req.Header.Set(disableFetchHeader, "true")
	}
	r, err := ctxhttp.Do(ctx, c.httpClient, req)
	if err != nil {
		return fmt.Errorf("ctxhttp.Do(ctx, client, %q): %v", u, err)
	}
	defer r.Body.Close()
	if err := responseError(r, disableFetch); err != nil {
		return err
	}
	return bodyFunc(r.Body)
}

// responseError translates the response status code to an appropriate error.
func responseError(r *http.Response, fetchDisabled bool) error {
	switch {
	case 200 <= r.StatusCode && r.StatusCode < 300:
		return nil
	case r.StatusCode == http.StatusNotFound,
		r.StatusCode == http.StatusGone:
		// Treat both 404 Not Found and 410 Gone responses
		// from the proxy as a "not found" error category.
		// If the response body contains "fetch timed out", treat this
		// as a 504 response so that we retry fetching the module version again
		// later.
		//
		// If the Disable-Module-Fetch header was set, use a different
		// error code so we can tell the difference.
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return fmt.Errorf("ioutil.readall: %v", err)
		}
		d := string(data)
		switch {
		case strings.Contains(d, "fetch timed out"):
			err = derrors.ProxyTimedOut
		case fetchDisabled:
			err = derrors.NotFetched
		default:
			err = derrors.NotFound
		}
		return fmt.Errorf("%q: %w", d, err)
	default:
		return fmt.Errorf("unexpected status %d %s", r.StatusCode, r.Status)
	}
}
<commit_msg>internal/proxy: do not report not found errors<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package proxy provides a client for interacting with a proxy.
package proxy

import (
	"archive/zip"
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"go.opencensus.io/plugin/ochttp"
	"golang.org/x/mod/module"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
)

// A Client is used by the fetch service to communicate with a module
// proxy. It handles all methods defined by go help goproxy.
type Client struct {
	// URL of the module proxy web server
	url string

	// client used for HTTP requests. It is mutable for testing purposes.
	httpClient *http.Client
}

// A VersionInfo contains metadata about a given version of a module.
type VersionInfo struct {
	Version string
	Time    time.Time
}

// Setting this header to true prevents the proxy from fetching uncached
// modules.
const disableFetchHeader = "Disable-Module-Fetch"

// New constructs a *Client using the provided url, which is expected to
// be an absolute URI that can be directly passed to http.Get.
func New(u string) (_ *Client, err error) {
	defer derrors.Wrap(&err, "proxy.New(%q)", u)
	return &Client{
		url:        strings.TrimRight(u, "/"),
		httpClient: &http.Client{Transport: &ochttp.Transport{}},
	}, nil
}

// GetInfo makes a request to $GOPROXY/<module>/@v/<requestedVersion>.info and
// transforms that data into a *VersionInfo.
func (c *Client) GetInfo(ctx context.Context, modulePath, requestedVersion string) (_ *VersionInfo, err error) {
	return c.getInfo(ctx, modulePath, requestedVersion, false)
}

// GetInfoNoFetch behaves like GetInfo, except that it sets the
// Disable-Module-Fetch header so that the proxy does not fetch a module it
// doesn't already know about.
func (c *Client) GetInfoNoFetch(ctx context.Context, modulePath, requestedVersion string) (_ *VersionInfo, err error) {
	return c.getInfo(ctx, modulePath, requestedVersion, true)
}

func (c *Client) getInfo(ctx context.Context, modulePath, requestedVersion string, disableFetch bool) (_ *VersionInfo, err error) {
	defer func() {
		wrap := derrors.Wrap
		if !errors.Is(err, derrors.NotFound) {
			wrap = derrors.WrapAndReport
		}
		wrap(&err, "proxy.Client.getInfo(%q, %q)", modulePath, requestedVersion)
	}()
	data, err := c.readBody(ctx, modulePath, requestedVersion, "info", disableFetch)
	if err != nil {
		return nil, err
	}
	var v VersionInfo
	if err := json.Unmarshal(data, &v); err != nil {
		return nil, err
	}
	return &v, nil
}

// GetMod makes a request to $GOPROXY/<module>/@v/<resolvedVersion>.mod and returns the raw data.
func (c *Client) GetMod(ctx context.Context, modulePath, resolvedVersion string) (_ []byte, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetMod(%q, %q)", modulePath, resolvedVersion)
	return c.readBody(ctx, modulePath, resolvedVersion, "mod", false)
}

// GetZip makes a request to $GOPROXY/<modulePath>/@v/<resolvedVersion>.zip and
// transforms that data into a *zip.Reader. <resolvedVersion> must have already
// been resolved by first making a request to
// $GOPROXY/<modulePath>/@v/<requestedVersion>.info to obtained the valid
// semantic version.
func (c *Client) GetZip(ctx context.Context, modulePath, resolvedVersion string) (_ *zip.Reader, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetZip(ctx, %q, %q)", modulePath, resolvedVersion)

	bodyBytes, err := c.readBody(ctx, modulePath, resolvedVersion, "zip", false)
	if err != nil {
		return nil, err
	}
	zipReader, err := zip.NewReader(bytes.NewReader(bodyBytes), int64(len(bodyBytes)))
	if err != nil {
		return nil, fmt.Errorf("zip.NewReader: %v: %w", err, derrors.BadModule)
	}
	return zipReader, nil
}

// GetZipSize gets the size in bytes of the zip from the proxy, without downloading it.
// The version must be resolved, as by a call to Client.GetInfo.
func (c *Client) GetZipSize(ctx context.Context, modulePath, resolvedVersion string) (_ int64, err error) {
	defer derrors.WrapAndReport(&err, "proxy.Client.GetZipSize(ctx, %q, %q)", modulePath, resolvedVersion)

	url, err := c.escapedURL(modulePath, resolvedVersion, "zip")
	if err != nil {
		return 0, err
	}
	res, err := ctxhttp.Head(ctx, c.httpClient, url)
	if err != nil {
		return 0, fmt.Errorf("ctxhttp.Head(ctx, client, %q): %v", url, err)
	}
	defer res.Body.Close()
	if err := responseError(res, false); err != nil {
		return 0, err
	}
	if res.ContentLength < 0 {
		return 0, errors.New("unknown content length")
	}
	return res.ContentLength, nil
}

func (c *Client) escapedURL(modulePath, requestedVersion, suffix string) (_ string, err error) {
	defer func() {
		derrors.Wrap(&err, "Client.escapedURL(%q, %q, %q)", modulePath, requestedVersion, suffix)
	}()

	if suffix != "info" && suffix != "mod" && suffix != "zip" {
		return "", errors.New(`suffix must be "info", "mod" or "zip"`)
	}
	escapedPath, err := module.EscapePath(modulePath)
	if err != nil {
		return "", fmt.Errorf("path: %v: %w", err, derrors.InvalidArgument)
	}
	if requestedVersion == internal.LatestVersion {
		if suffix != "info" {
			return "", fmt.Errorf("cannot ask for latest with suffix %q", suffix)
		}
		return fmt.Sprintf("%s/%s/@latest", c.url, escapedPath), nil
	}
	escapedVersion, err := module.EscapeVersion(requestedVersion)
	if err != nil {
		return "", fmt.Errorf("version: %v: %w", err, derrors.InvalidArgument)
	}
	return fmt.Sprintf("%s/%s/@v/%s.%s", c.url, escapedPath, escapedVersion, suffix), nil
}

func (c *Client) readBody(ctx context.Context, modulePath, requestedVersion, suffix string, disableFetch bool) (_ []byte, err error) {
	defer derrors.Wrap(&err, "Client.readBody(%q, %q, %q)", modulePath, requestedVersion, suffix)

	u, err := c.escapedURL(modulePath, requestedVersion, suffix)
	if err != nil {
		return nil, err
	}
	var data []byte
	err = c.executeRequest(ctx, u, disableFetch, func(body io.Reader) error {
		var err error
		data, err = ioutil.ReadAll(body)
		return err
	})
	if err != nil {
		return nil, err
	}
	return data, nil
}

// ListVersions makes a request to $GOPROXY/<path>/@v/list and returns the
// resulting version strings.
func (c *Client) ListVersions(ctx context.Context, modulePath string) ([]string, error) {
	escapedPath, err := module.EscapePath(modulePath)
	if err != nil {
		return nil, fmt.Errorf("module.EscapePath(%q): %w", modulePath, derrors.InvalidArgument)
	}
	u := fmt.Sprintf("%s/%s/@v/list", c.url, escapedPath)
	var versions []string
	collect := func(body io.Reader) error {
		scanner := bufio.NewScanner(body)
		for scanner.Scan() {
			versions = append(versions, scanner.Text())
		}
		return scanner.Err()
	}
	if err := c.executeRequest(ctx, u, false, collect); err != nil {
		return nil, err
	}
	return versions, nil
}

// executeRequest executes an HTTP GET request for u, then calls the bodyFunc
// on the response body, if no error occurred.
func (c *Client) executeRequest(ctx context.Context, u string, disableFetch bool, bodyFunc func(body io.Reader) error) (err error) {
	defer func() {
		if ctx.Err() != nil {
			err = fmt.Errorf("%v: %w", err, derrors.ProxyTimedOut)
		}
		derrors.Wrap(&err, "executeRequest(ctx, %q)", u)
	}()

	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	if disableFetch {
		req.Header.Set(disableFetchHeader, "true")
	}
	r, err := ctxhttp.Do(ctx, c.httpClient, req)
	if err != nil {
		return fmt.Errorf("ctxhttp.Do(ctx, client, %q): %v", u, err)
	}
	defer r.Body.Close()
	if err := responseError(r, disableFetch); err != nil {
		return err
	}
	return bodyFunc(r.Body)
}

// responseError translates the response status code to an appropriate error.
func responseError(r *http.Response, fetchDisabled bool) error {
	switch {
	case 200 <= r.StatusCode && r.StatusCode < 300:
		return nil
	case r.StatusCode == http.StatusNotFound,
		r.StatusCode == http.StatusGone:
		// Treat both 404 Not Found and 410 Gone responses
		// from the proxy as a "not found" error category.
		// If the response body contains "fetch timed out", treat this
		// as a 504 response so that we retry fetching the module version again
		// later.
		//
		// If the Disable-Module-Fetch header was set, use a different
		// error code so we can tell the difference.
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return fmt.Errorf("ioutil.readall: %v", err)
		}
		d := string(data)
		switch {
		case strings.Contains(d, "fetch timed out"):
			err = derrors.ProxyTimedOut
		case fetchDisabled:
			err = derrors.NotFetched
		default:
			err = derrors.NotFound
		}
		return fmt.Errorf("%q: %w", d, err)
	default:
		return fmt.Errorf("unexpected status %d %s", r.StatusCode, r.Status)
	}
}
<|endoftext|>


<commit_before>package glisp

import (
	"errors"
)

var WrongNargs error = errors.New("wrong number of arguments")

type GlispFunction []Instruction
type GlispUserFunction func(*Glisp, string, []Sexp) (Sexp, error)

func (f GlispFunction) SexpString() string {
	return "function"
}

func (f GlispUserFunction) SexpString() string {
	return "user_function"
}

func CompareFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	res, err := Compare(args[0], args[1])
	if err != nil {
		return SexpNull, err
	}

	cond := false
	switch name {
	case "<":
		cond = res < 0
	case ">":
		cond = res > 0
	case "<=":
		cond = res <= 0
	case ">=":
		cond = res >= 0
	case "=":
		cond = res == 0
	case "not=":
		cond = res != 0
	}

	return SexpBool(cond), nil
}

func BinaryIntFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	var op IntegerOp
	switch name {
	case "sll":
		op = ShiftLeft
	case "sra":
		op = ShiftRightArith
	case "srl":
		op = ShiftRightLog
	case "mod":
		op = Modulo
	}

	return IntegerDo(op, args[0], args[1])
}

func BitwiseFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	var op IntegerOp
	switch name {
	case "bit-and":
		op = BitAnd
	case "bit-or":
		op = BitOr
	case "bit-xor":
		op = BitXor
	}

	accum := args[0]
	var err error

	for _, expr := range args[1:] {
		accum, err = IntegerDo(op, accum, expr)
		if err != nil {
			return SexpNull, err
		}
	}
	return accum, nil
}

func NumericFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) < 1 {
		return SexpNull, WrongNargs
	}

	var err error
	accum := args[0]
	var op NumericOp
	switch name {
	case "+":
		op = Add
	case "-":
		op = Sub
	case "*":
		op = Mult
	case "/":
		op = Div
	}

	for _, expr := range args[1:] {
		accum, err = NumericDo(op, accum, expr)
		if err != nil {
			return SexpNull, err
		}
	}
	return accum, nil
}

var BuiltinFunctions = map[string]GlispUserFunction {
	"<" : CompareFunction,
	">" : CompareFunction,
	"<=": CompareFunction,
	">=": CompareFunction,
	"=" : CompareFunction,
	"not=": CompareFunction,
	"sll": BinaryIntFunction,
	"sra": BinaryIntFunction,
	"srl": BinaryIntFunction,
	"mod": BinaryIntFunction,
	"+": NumericFunction,
	"-": NumericFunction,
	"*": NumericFunction,
	"/": NumericFunction,
	"bit-and": BitwiseFunction,
	"bit-or": BitwiseFunction,
	"bit-xor": BitwiseFunction,
}
<commit_msg>implement read<commit_after>package glisp

import (
	"errors"
	"bytes"
)

var WrongNargs error = errors.New("wrong number of arguments")

type GlispFunction []Instruction
type GlispUserFunction func(*Glisp, string, []Sexp) (Sexp, error)

func (f GlispFunction) SexpString() string {
	return "function"
}

func (f GlispUserFunction) SexpString() string {
	return "user_function"
}

func CompareFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	res, err := Compare(args[0], args[1])
	if err != nil {
		return SexpNull, err
	}

	cond := false
	switch name {
	case "<":
		cond = res < 0
	case ">":
		cond = res > 0
	case "<=":
		cond = res <= 0
	case ">=":
		cond = res >= 0
	case "=":
		cond = res == 0
	case "not=":
		cond = res != 0
	}

	return SexpBool(cond), nil
}

func BinaryIntFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	var op IntegerOp
	switch name {
	case "sll":
		op = ShiftLeft
	case "sra":
		op = ShiftRightArith
	case "srl":
		op = ShiftRightLog
	case "mod":
		op = Modulo
	}

	return IntegerDo(op, args[0], args[1])
}

func BitwiseFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 2 {
		return SexpNull, WrongNargs
	}

	var op IntegerOp
	switch name {
	case "bit-and":
		op = BitAnd
	case "bit-or":
		op = BitOr
	case "bit-xor":
		op = BitXor
	}

	accum := args[0]
	var err error

	for _, expr := range args[1:] {
		accum, err = IntegerDo(op, accum, expr)
		if err != nil {
			return SexpNull, err
		}
	}
	return accum, nil
}

func NumericFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) < 1 {
		return SexpNull, WrongNargs
	}

	var err error
	accum := args[0]
	var op NumericOp
	switch name {
	case "+":
		op = Add
	case "-":
		op = Sub
	case "*":
		op = Mult
	case "/":
		op = Div
	}

	for _, expr := range args[1:] {
		accum, err = NumericDo(op, accum, expr)
		if err != nil {
			return SexpNull, err
		}
	}
	return accum, nil
}

func ReadFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
	if len(args) != 1 {
		return SexpNull, WrongNargs
	}
	str := ""
	switch t := args[0].(type) {
	case SexpStr:
		str = string(t)
	default:
		return SexpNull, WrongType
	}
	lexer := NewLexerFromStream(bytes.NewBuffer([]byte(str)))
	parser := Parser{lexer, env}
	return ParseExpression(&parser)
}

var BuiltinFunctions = map[string]GlispUserFunction {
	"<" : CompareFunction,
	">" : CompareFunction,
	"<=": CompareFunction,
	">=": CompareFunction,
	"=" : CompareFunction,
	"not=": CompareFunction,
	"sll": BinaryIntFunction,
	"sra": BinaryIntFunction,
	"srl": BinaryIntFunction,
	"mod": BinaryIntFunction,
	"+": NumericFunction,
	"-": NumericFunction,
	"*": NumericFunction,
	"/": NumericFunction,
	"bit-and": BitwiseFunction,
	"bit-or": BitwiseFunction,
	"bit-xor": BitwiseFunction,
	"read": ReadFunction,
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go Cloud 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
//
//     https://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 runtimevar_test contains tests that exercises the runtimevar APIs. It does not test
// driver implementations.
package runtimevar_test

import (
	"bytes"
	"context"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/google/go-cloud/runtimevar"
	"github.com/google/go-cloud/runtimevar/driver"
	"github.com/google/go-cmp/cmp"
)

// fakeWatcher is a fake implementation of driver.Watcher meant only for exercising the
// generic APIs via tests.
type fakeWatcher struct {
	dc driver.Variable
}

func (fw *fakeWatcher) Close() error {
	return nil
}

func (fw *fakeWatcher) WatchVariable(context.Context) (driver.Variable, error) {
	return fw.dc, nil
}

// Ensure that fakeWatcher implements driver.Watcher
var _ driver.Watcher = &fakeWatcher{}

func TestVariable(t *testing.T) {
	ctx := context.Background()

	dc1 := driver.Variable{
		Value:      42,
		UpdateTime: time.Now(),
	}
	fw := &fakeWatcher{dc1}
	cfg := runtimevar.New(fw)

	// Watch should return the initial variable.
	snap1, err := cfg.Watch(ctx)
	if err != nil {
		t.Fatalf("Variable.Watch returns error %v", err)
	}
	if got := snap1.Value.(int); got != dc1.Value {
		t.Errorf("Snapshot.Value got %v, want %v", got, dc1.Value)
	}
	if got := snap1.UpdateTime; got != dc1.UpdateTime {
		t.Errorf("Snapshot.UpdateTime got %v, want %v", got, dc1.UpdateTime)
	}

	// Update the watcher's return value for Watch.
	dc2 := driver.Variable{
		Value:      8080,
		UpdateTime: time.Now(),
	}
	fw.dc = dc2

	// Retrieve next value.
	snap2, err := cfg.Watch(ctx)
	if err != nil {
		t.Fatalf("Variable.Watch returns error %v", err)
	}
	if got := snap2.Value.(int); got != dc2.Value {
		t.Errorf("Snapshot.Value got %v, want %v", got, dc2.Value)
	}
	if got := snap2.UpdateTime; got != dc2.UpdateTime {
		t.Errorf("Snapshot.UpdateTime got %v, want %v", got, dc2.UpdateTime)
	}
}

func TestDecoder(t *testing.T) {
	type Struct struct {
		FieldA string
		FieldB map[string]interface{}
	}

	num := 4321
	numptr := &num
	str := "boring string"
	strptr := &str

	inputs := []interface{}{
		str,
		strptr,
		num,
		numptr,
		100.1,
		Struct{
			FieldA: "hello",
			FieldB: map[string]interface{}{
				"hello": "world",
			},
		},
		&Struct{
			FieldA: "world",
		},
		map[string]string{
			"slice": "pizza",
		},
		&map[string]interface{}{},
		[]string{"hello", "world"},
		&[]int{1, 0, 1},
		[...]float64{3.1415},
		&[...]int64{4, 5, 6},
	}

	for _, tc := range []struct {
		desc     string
		encodeFn func(interface{}) ([]byte, error)
		decodeFn runtimevar.Decode
	}{
		{
			desc:     "JSON",
			encodeFn: json.Marshal,
			decodeFn: runtimevar.JSONDecode,
		},
		{
			desc:     "Gob",
			encodeFn: gobMarshal,
			decodeFn: runtimevar.GobDecode,
		},
	} {
		for i, input := range inputs {
			t.Run(fmt.Sprintf("%s_%d", tc.desc, i), func(t *testing.T) {
				decoder := runtimevar.NewDecoder(input, tc.decodeFn)
				b, err := tc.encodeFn(input)
				if err != nil {
					t.Fatalf("marshal error %v", err)
				}
				got, err := decoder.Decode(b)
				if err != nil {
					t.Fatalf("parse input\n%s\nerror: %v", string(b), err)
				}
				if reflect.TypeOf(got) != reflect.TypeOf(input) {
					t.Errorf("type mismatch got %T, want %T", got, input)
				}
				if diff := cmp.Diff(got, input); diff != "" {
					t.Errorf("value diff:\n%v", diff)
				}
			})
		}
	}
}

func gobMarshal(v interface{}) ([]byte, error) {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(v); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func TestStringDecoder(t *testing.T) {
	input := "hello world"
	got, err := runtimevar.StringDecoder.Decode([]byte(input))
	if err != nil {
		t.Fatalf("error: %v", err)
	}
	if input != got.(string) {
		t.Errorf("output got %v, want %q", got, input)
	}
}

func TestBytesDecoder(t *testing.T) {
	input := []byte("hello world")
	got, err := runtimevar.BytesDecoder.Decode(input)
	if err != nil {
		t.Fatalf("error: %v", err)
	}
	if diff := cmp.Diff(got, input); diff != "" {
		t.Errorf("output got %v, want %q", got, input)
	}
}
<commit_msg>runtimevar: remove unnecessary stub test (#364)<commit_after>// Copyright 2018 The Go Cloud 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
//
//     https://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 runtimevar_test contains tests that exercises the runtimevar APIs. It does not test
// driver implementations.
package runtimevar_test

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"reflect"
	"testing"

	"github.com/google/go-cloud/runtimevar"
	"github.com/google/go-cmp/cmp"
)

func TestDecoder(t *testing.T) {
	type Struct struct {
		FieldA string
		FieldB map[string]interface{}
	}

	num := 4321
	numptr := &num
	str := "boring string"
	strptr := &str

	inputs := []interface{}{
		str,
		strptr,
		num,
		numptr,
		100.1,
		Struct{
			FieldA: "hello",
			FieldB: map[string]interface{}{
				"hello": "world",
			},
		},
		&Struct{
			FieldA: "world",
		},
		map[string]string{
			"slice": "pizza",
		},
		&map[string]interface{}{},
		[]string{"hello", "world"},
		&[]int{1, 0, 1},
		[...]float64{3.1415},
		&[...]int64{4, 5, 6},
	}

	for _, tc := range []struct {
		desc     string
		encodeFn func(interface{}) ([]byte, error)
		decodeFn runtimevar.Decode
	}{
		{
			desc:     "JSON",
			encodeFn: json.Marshal,
			decodeFn: runtimevar.JSONDecode,
		},
		{
			desc:     "Gob",
			encodeFn: gobMarshal,
			decodeFn: runtimevar.GobDecode,
		},
	} {
		for i, input := range inputs {
			t.Run(fmt.Sprintf("%s_%d", tc.desc, i), func(t *testing.T) {
				decoder := runtimevar.NewDecoder(input, tc.decodeFn)
				b, err := tc.encodeFn(input)
				if err != nil {
					t.Fatalf("marshal error %v", err)
				}
				got, err := decoder.Decode(b)
				if err != nil {
					t.Fatalf("parse input\n%s\nerror: %v", string(b), err)
				}
				if reflect.TypeOf(got) != reflect.TypeOf(input) {
					t.Errorf("type mismatch got %T, want %T", got, input)
				}
				if diff := cmp.Diff(got, input); diff != "" {
					t.Errorf("value diff:\n%v", diff)
				}
			})
		}
	}
}

func gobMarshal(v interface{}) ([]byte, error) {
	var buf bytes.Buffer
	if err := gob.NewEncoder(&buf).Encode(v); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func TestStringDecoder(t *testing.T) {
	input := "hello world"
	got, err := runtimevar.StringDecoder.Decode([]byte(input))
	if err != nil {
		t.Fatalf("error: %v", err)
	}
	if input != got.(string) {
		t.Errorf("output got %v, want %q", got, input)
	}
}

func TestBytesDecoder(t *testing.T) {
	input := []byte("hello world")
	got, err := runtimevar.BytesDecoder.Decode(input)
	if err != nil {
		t.Fatalf("error: %v", err)
	}
	if diff := cmp.Diff(got, input); diff != "" {
		t.Errorf("output got %v, want %q", got, input)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 statfs_test

import (
	"bytes"
	"fmt"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"syscall"
	"testing"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/fuse/samples/statfs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestStatFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func convertName(in []int8) (s string) {
	var tmp []byte
	for _, v := range in {
		if v == 0 {
			break
		}

		tmp = append(tmp, byte(v))
	}

	s = string(tmp)
	return
}

// Ask `df` for statistics about the file system's capacity and free space,
// useful for checking that our reading of statfs(2) output matches the
// system's. The output is not guaranteed to have resolution greater than 2^10
// (1 KiB).
func df(dir string) (capacity, used, available uint64, err error) {
	// Sample output:
	//
	//     Filesystem  1024-blocks Used Available Capacity iused ifree %iused  Mounted on
	//     fake@bucket          32   16        16    50%       0     0  100%   /Users/jacobsa/tmp/mp
	//
	re := regexp.MustCompile(`^\S+\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s+\d+\s+\d+\s+\d+%.*$`)

	// Call df with a block size of 1024 and capture its output.
	cmd := exec.Command("df", dir)
	cmd.Env = []string{"BLOCKSIZE=1024"}

	output, err := cmd.CombinedOutput()
	if err != nil {
		return
	}

	// Scrape it.
	for _, line := range bytes.Split(output, []byte{'\n'}) {
		// Is this the line we're interested in?
		if !bytes.Contains(line, []byte(dir)) {
			continue
		}

		submatches := re.FindSubmatch(line)
		if submatches == nil {
			err = fmt.Errorf("Unable to parse line: %q", line)
			return
		}

		capacity, err = strconv.ParseUint(string(submatches[1]), 10, 64)
		if err != nil {
			return
		}

		used, err = strconv.ParseUint(string(submatches[2]), 10, 64)
		if err != nil {
			return
		}

		available, err = strconv.ParseUint(string(submatches[3]), 10, 64)
		if err != nil {
			return
		}

		return
	}

	err = fmt.Errorf("Unable to parse df output:\n%s", output)
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type StatFSTest struct {
	samples.SampleTest
	fs statfs.FS

	// t.Dir, with symlinks resolved and redundant path components removed.
	canonicalDir string
}

var _ SetUpInterface = &StatFSTest{}
var _ TearDownInterface = &StatFSTest{}

func init() { RegisterTestSuite(&StatFSTest{}) }

func (t *StatFSTest) SetUp(ti *TestInfo) {
	var err error

	// Writeback caching can ruin our measurement of the write sizes the kernel
	// decides to give us, since it causes write acking to race against writes
	// being issued from the client.
	t.MountConfig.DisableWritebackCaching = true

	// Create the file system.
	t.fs = statfs.New()
	t.Server = fuseutil.NewFileSystemServer(t.fs)

	// Mount it.
	t.SampleTest.SetUp(ti)

	// Canonicalize the mount point.
	t.canonicalDir, err = filepath.EvalSymlinks(t.Dir)
	AssertEq(nil, err)
	t.canonicalDir = path.Clean(t.canonicalDir)
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *StatFSTest) Syscall_ZeroValues() {
	var err error
	var stat syscall.Statfs_t

	// Call without configuring a canned response, meaning the OS will see the
	// zero value for each field. The assertions below act as documentation for
	// the OS's behavior in this case.
	err = syscall.Statfs(t.Dir, &stat)
	AssertEq(nil, err)

	ExpectEq(4096, stat.Bsize)
	ExpectEq(65536, stat.Iosize)
	ExpectEq(0, stat.Blocks)
	ExpectEq(0, stat.Bfree)
	ExpectEq(0, stat.Bavail)
	ExpectEq(0, stat.Files)
	ExpectEq(0, stat.Ffree)
	ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
	ExpectThat(
		convertName(stat.Mntfromname[:]),
		MatchesRegexp(`mount_osxfusefs@osxfuse\d+`))
}

func (t *StatFSTest) Syscall_NonZeroValues() {
	var err error
	var stat syscall.Statfs_t

	// Set up the canned response.
	canned := fuseops.StatFSOp{
		BlockSize: 1 << 15,

		Blocks:          1<<51 + 3,
		BlocksFree:      1<<43 + 5,
		BlocksAvailable: 1<<41 + 7,

		Inodes:     1<<59 + 11,
		InodesFree: 1<<58 + 13,
	}

	t.fs.SetStatFSResponse(canned)

	// Stat.
	err = syscall.Statfs(t.Dir, &stat)
	AssertEq(nil, err)

	ExpectEq(4096, stat.Bsize) // OS X seems to always make this 4096.
	ExpectEq(canned.BlockSize, stat.Iosize)
	ExpectEq(canned.Blocks, stat.Blocks)
	ExpectEq(canned.BlocksFree, stat.Bfree)
	ExpectEq(canned.BlocksAvailable, stat.Bavail)
	ExpectEq(canned.Inodes, stat.Files)
	ExpectEq(canned.InodesFree, stat.Ffree)
	ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
	ExpectThat(
		convertName(stat.Mntfromname[:]),
		MatchesRegexp(`mount_osxfusefs@osxfuse\d+`))
}

func (t *StatFSTest) CapacityAndFreeSpace() {
	canned := fuseops.StatFSOp{
		Blocks:          1024,
		BlocksFree:      896,
		BlocksAvailable: 768,
	}

	// Check that df agrees with us about a range of block sizes.
	for log2BlockSize := uint(9); log2BlockSize < 31; log2BlockSize++ {
		bs := uint64(1) << log2BlockSize
		desc := fmt.Sprintf("block size: %d (2^%d)", bs, log2BlockSize)

		// Set up the canned response.
		canned.BlockSize = uint32(bs)
		t.fs.SetStatFSResponse(canned)

		// Call df.
		capacity, used, available, err := df(t.canonicalDir)
		AssertEq(nil, err)

		ExpectEq(bs*canned.Blocks, capacity, "%s", desc)
		ExpectEq(bs*(canned.Blocks-canned.BlocksAvailable), used, "%s", desc)
		ExpectEq(bs*canned.BlocksAvailable, available, "%s", desc)
	}
}

func (t *StatFSTest) WriteSize() {
	AssertTrue(false, "TODO")
}

func (t *StatFSTest) UnsupportedBlockSizes() {
	AssertTrue(false, "TODO")
}
<commit_msg>StatFSTest.WriteSize<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 statfs_test

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"syscall"
	"testing"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/fuse/samples/statfs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestStatFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func convertName(in []int8) (s string) {
	var tmp []byte
	for _, v := range in {
		if v == 0 {
			break
		}

		tmp = append(tmp, byte(v))
	}

	s = string(tmp)
	return
}

// Ask `df` for statistics about the file system's capacity and free space,
// useful for checking that our reading of statfs(2) output matches the
// system's. The output is not guaranteed to have resolution greater than 2^10
// (1 KiB).
func df(dir string) (capacity, used, available uint64, err error) {
	// Sample output:
	//
	//     Filesystem  1024-blocks Used Available Capacity iused ifree %iused  Mounted on
	//     fake@bucket          32   16        16    50%       0     0  100%   /Users/jacobsa/tmp/mp
	//
	re := regexp.MustCompile(`^\S+\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+%\s+\d+\s+\d+\s+\d+%.*$`)

	// Call df with a block size of 1024 and capture its output.
	cmd := exec.Command("df", dir)
	cmd.Env = []string{"BLOCKSIZE=1024"}

	output, err := cmd.CombinedOutput()
	if err != nil {
		return
	}

	// Scrape it.
	for _, line := range bytes.Split(output, []byte{'\n'}) {
		// Is this the line we're interested in?
		if !bytes.Contains(line, []byte(dir)) {
			continue
		}

		submatches := re.FindSubmatch(line)
		if submatches == nil {
			err = fmt.Errorf("Unable to parse line: %q", line)
			return
		}

		capacity, err = strconv.ParseUint(string(submatches[1]), 10, 64)
		if err != nil {
			return
		}

		used, err = strconv.ParseUint(string(submatches[2]), 10, 64)
		if err != nil {
			return
		}

		available, err = strconv.ParseUint(string(submatches[3]), 10, 64)
		if err != nil {
			return
		}

		return
	}

	err = fmt.Errorf("Unable to parse df output:\n%s", output)
	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type StatFSTest struct {
	samples.SampleTest
	fs statfs.FS

	// t.Dir, with symlinks resolved and redundant path components removed.
	canonicalDir string
}

var _ SetUpInterface = &StatFSTest{}
var _ TearDownInterface = &StatFSTest{}

func init() { RegisterTestSuite(&StatFSTest{}) }

func (t *StatFSTest) SetUp(ti *TestInfo) {
	var err error

	// Writeback caching can ruin our measurement of the write sizes the kernel
	// decides to give us, since it causes write acking to race against writes
	// being issued from the client.
	t.MountConfig.DisableWritebackCaching = true

	// Create the file system.
	t.fs = statfs.New()
	t.Server = fuseutil.NewFileSystemServer(t.fs)

	// Mount it.
	t.SampleTest.SetUp(ti)

	// Canonicalize the mount point.
	t.canonicalDir, err = filepath.EvalSymlinks(t.Dir)
	AssertEq(nil, err)
	t.canonicalDir = path.Clean(t.canonicalDir)
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *StatFSTest) Syscall_ZeroValues() {
	var err error
	var stat syscall.Statfs_t

	// Call without configuring a canned response, meaning the OS will see the
	// zero value for each field. The assertions below act as documentation for
	// the OS's behavior in this case.
	err = syscall.Statfs(t.Dir, &stat)
	AssertEq(nil, err)

	ExpectEq(4096, stat.Bsize)
	ExpectEq(65536, stat.Iosize)
	ExpectEq(0, stat.Blocks)
	ExpectEq(0, stat.Bfree)
	ExpectEq(0, stat.Bavail)
	ExpectEq(0, stat.Files)
	ExpectEq(0, stat.Ffree)
	ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
	ExpectThat(
		convertName(stat.Mntfromname[:]),
		MatchesRegexp(`mount_osxfusefs@osxfuse\d+`))
}

func (t *StatFSTest) Syscall_NonZeroValues() {
	var err error
	var stat syscall.Statfs_t

	// Set up the canned response.
	canned := fuseops.StatFSOp{
		BlockSize: 1 << 15,

		Blocks:          1<<51 + 3,
		BlocksFree:      1<<43 + 5,
		BlocksAvailable: 1<<41 + 7,

		Inodes:     1<<59 + 11,
		InodesFree: 1<<58 + 13,
	}

	t.fs.SetStatFSResponse(canned)

	// Stat.
	err = syscall.Statfs(t.Dir, &stat)
	AssertEq(nil, err)

	ExpectEq(4096, stat.Bsize) // OS X seems to always make this 4096.
	ExpectEq(canned.BlockSize, stat.Iosize)
	ExpectEq(canned.Blocks, stat.Blocks)
	ExpectEq(canned.BlocksFree, stat.Bfree)
	ExpectEq(canned.BlocksAvailable, stat.Bavail)
	ExpectEq(canned.Inodes, stat.Files)
	ExpectEq(canned.InodesFree, stat.Ffree)
	ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
	ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
	ExpectThat(
		convertName(stat.Mntfromname[:]),
		MatchesRegexp(`mount_osxfusefs@osxfuse\d+`))
}

func (t *StatFSTest) CapacityAndFreeSpace() {
	canned := fuseops.StatFSOp{
		Blocks:          1024,
		BlocksFree:      896,
		BlocksAvailable: 768,
	}

	// Check that df agrees with us about a range of block sizes.
	for log2BlockSize := uint(9); log2BlockSize < 31; log2BlockSize++ {
		bs := uint64(1) << log2BlockSize
		desc := fmt.Sprintf("block size: %d (2^%d)", bs, log2BlockSize)

		// Set up the canned response.
		canned.BlockSize = uint32(bs)
		t.fs.SetStatFSResponse(canned)

		// Call df.
		capacity, used, available, err := df(t.canonicalDir)
		AssertEq(nil, err)

		ExpectEq(bs*canned.Blocks, capacity, "%s", desc)
		ExpectEq(bs*(canned.Blocks-canned.BlocksAvailable), used, "%s", desc)
		ExpectEq(bs*canned.BlocksAvailable, available, "%s", desc)
	}
}

func (t *StatFSTest) WriteSize() {
	var err error

	// Set up a smallish block size.
	canned := fuseops.StatFSOp{
		BlockSize:       8192,
		Blocks:          1234,
		BlocksFree:      1234,
		BlocksAvailable: 1234,
	}

	t.fs.SetStatFSResponse(canned)

	// Cause a large amount of date to be written.
	err = ioutil.WriteFile(
		path.Join(t.Dir, "foo"),
		bytes.Repeat([]byte{'x'}, 1<<22),
		0400)

	AssertEq(nil, err)

	// Despite the small block size, the OS shouldn't have given us pitifully
	// small chunks of data.
	ExpectEq(1<<20, t.fs.MostRecentWriteSize())
}

func (t *StatFSTest) UnsupportedBlockSizes() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package victor

import (
    "log"
    "regexp"
    "strings"
)

type Brain struct {
    name     string
    options  map[string]string
    matchers []*Matcher
    users    []*User
}

func NewBrain(name string) *Brain {
    brain := &Brain{
        name:     name,
        matchers: make([]*Matcher, 0, 1),
    }

    registerDefaultAbilities(brain)

    return brain
}

// AddMatcher adds a Matcher to the Brain's list of matching patterns.
func (self *Brain) AddMatcher(m *Matcher) {
    self.matchers = append(self.matchers, m)

    log.Printf("Pattern: /%s/", m.Pattern.String())
}

// Hear creates and registers a new Matcher with the Brain that is triggered
// when the pattern matches anything said in the room.
func (self *Brain) Hear(expStr string, callback func(*Context)) {
    exp, _ := regexp.Compile(expStr)

    self.AddMatcher(NewMatcher(exp, callback))
}

// Respond creates and registers a new Matcher with the Brain that is triggered
// when the pattern matches a statement directed at the bot specifically.
func (self *Brain) Respond(expStr string, callback func(*Context)) {
    expWithNameStr := "^(" + self.name + "[:,]?)\\s*(?:" + expStr + ")"
    exp, _ := regexp.Compile(strings.ToLower(expWithNameStr))

    self.AddMatcher(NewMatcher(exp, callback))
}

// Receive takes input from the service adapter and tests it against
// all registered Matchers.
func (self *Brain) Receive(ctx *Context) {
    for _, matcher := range self.matchers {
        if matcher.Test(ctx) {
            matcher.Callback(ctx)
        }
    }
}

// Instructs the Brain to remember the information about a user.
// Passing it a user that it has already seen will update the info
// for the same user in memory.
func (self *Brain) RememberUser(user *User) {
    for i, u := range self.users {
        if u.Id == user.Id {
            // update the name if its different
            if u.Name != user.Name {
                self.users[i].Name = user.Name
            }

            return
        }
    }

    self.users = append(self.users, user)
}

// Returns a list of all known users.
func (self *Brain) Users() []*User {
    return self.users
}

// Returns the User with the specified ID from memory.
func (self *Brain) UserForId(id int) *User {
    for _, user := range self.users {
        if user.Id == id {
            return user
        }
    }

    return nil
}
<commit_msg>Proper case-insensitivity flag for regexp pattern.<commit_after>package victor

import (
    "log"
    "regexp"
)

type Brain struct {
    name     string
    options  map[string]string
    matchers []*Matcher
    users    []*User
}

func NewBrain(name string) *Brain {
    brain := &Brain{
        name:     name,
        matchers: make([]*Matcher, 0, 1),
    }

    registerDefaultAbilities(brain)

    return brain
}

// AddMatcher adds a Matcher to the Brain's list of matching patterns.
func (self *Brain) AddMatcher(m *Matcher) {
    self.matchers = append(self.matchers, m)

    log.Printf("Pattern: /%s/", m.Pattern.String())
}

// Hear creates and registers a new Matcher with the Brain that is triggered
// when the pattern matches anything said in the room.
func (self *Brain) Hear(expStr string, callback func(*Context)) {
    exp, _ := regexp.Compile(expStr)

    self.AddMatcher(NewMatcher(exp, callback))
}

// Respond creates and registers a new Matcher with the Brain that is triggered
// when the pattern matches a statement directed at the bot specifically.
func (self *Brain) Respond(expStr string, callback func(*Context)) {
    expWithNameStr := "(?i)^(" + self.name + "[:,]?)\\s*(?:" + expStr + ")"
    exp, _ := regexp.Compile(expWithNameStr)

    self.AddMatcher(NewMatcher(exp, callback))
}

// Receive takes input from the service adapter and tests it against
// all registered Matchers.
func (self *Brain) Receive(ctx *Context) {
    for _, matcher := range self.matchers {
        if matcher.Test(ctx) {
            matcher.Callback(ctx)
        }
    }
}

// Instructs the Brain to remember the information about a user.
// Passing it a user that it has already seen will update the info
// for the same user in memory.
func (self *Brain) RememberUser(user *User) {
    for i, u := range self.users {
        if u.Id == user.Id {
            // update the name if its different
            if u.Name != user.Name {
                self.users[i].Name = user.Name
            }

            return
        }
    }

    self.users = append(self.users, user)
}

// Returns a list of all known users.
func (self *Brain) Users() []*User {
    return self.users
}

// Returns the User with the specified ID from memory.
func (self *Brain) UserForId(id int) *User {
    for _, user := range self.users {
        if user.Id == id {
            return user
        }
    }

    return nil
}
<|endoftext|>


<commit_before>package dnsserver

import (
	"fmt"
	"net"
	"testing"

	"github.com/miekg/dns"
)

// 53 could be in use by a local cache or w/e. 5353 is in use by avahi mDNS on
// my machine. Basically I don't want to have to write a port allocator.
const service = "127.0.0.1:5300"

var server = NewDNSServer("docker")

func init() {
	go func() {
		err := server.Listen(service)
		if err != nil {
			panic(err)
		}
	}()
}

func msgClient(fqdn string, dnsType uint16) (*dns.Msg, error) {
	m := new(dns.Msg)
	m.SetQuestion(fqdn, dnsType)
	return dns.Exchange(m, service)
}

func TestARecordCRUD(t *testing.T) {
	table := map[string]net.IP{
		"test":  net.ParseIP("127.0.0.2"),
		"test2": net.ParseIP("127.0.0.3"),
	}

	// do this in independent parts so both records exist. This tests some
	// collision issues.
	for host, ip := range table {
		server.SetA(host, ip)
	}

	for host, ip := range table {
		msg, err := msgClient(fmt.Sprintf("%s.docker.", host), dns.TypeA)

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

		if len(msg.Answer) != 1 {
			t.Fatalf("Server did not reply with a valid answer.")
		}

		if msg.Answer[0].Header().Ttl != 1 {
			t.Fatalf("TTL was %d instead of 1", msg.Answer[0].Header().Ttl)
		}

		if msg.Answer[0].Header().Rrtype != dns.TypeA {
			t.Fatalf("Expected A record, got a record of type %d instead.", msg.Answer[0].Header().Rrtype)
		}

		if msg.Answer[0].Header().Name != fmt.Sprintf("%s.docker.", host) {
			// If this fails, we probably need to look at miekg/dns as this should
			// not be possible.
			t.Fatalf("Name does not match query sent, %q was provided", msg.Answer[0].Header().Name)
		}

		aRecord := msg.Answer[0].(*dns.A).A
		if !aRecord.Equal(ip) {
			t.Fatalf("IP %q does not match registered IP %q", aRecord, ip)
		}
	}

	for host := range table {
		server.DeleteA(host)
	}

	for host := range table {
		msg, err := msgClient(fmt.Sprintf("%s.docker.", host), dns.TypeA)

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

		if len(msg.Answer) != 0 {
			t.Fatal("Server gave a reply after record has been deleted")
		}
	}
}

func TestSRVRecordCRUD(t *testing.T) {
	table := map[string][]SRVRecord{
		"test":  {{80, "test.docker."}},
		"test2": {{81, "test2.docker."}},
	}

	// do this in independent parts so both records exist. This tests some
	// collision issues.
	for name, srv := range table {
		server.SetSRV(name, "tcp", srv)
	}

	for name, srv := range table {
		msg, err := msgClient(fmt.Sprintf("_%s._tcp.docker.", name), dns.TypeSRV)

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

		if len(msg.Answer) != 1 {
			t.Fatalf("Server did not reply with a valid answer.")
		}

		if msg.Answer[0].Header().Ttl != 1 {
			t.Fatalf("TTL was %d instead of 1", msg.Answer[0].Header().Ttl)
		}

		if msg.Answer[0].Header().Rrtype != dns.TypeSRV {
			t.Fatalf("Expected SRV record, got a record of type %d instead.", msg.Answer[0].Header().Rrtype)
		}

		if msg.Answer[0].Header().Name != fmt.Sprintf("_%s._tcp.docker.", name) {
			// If this fails, we probably need to look at miekg/dns as this should
			// not be possible.
			t.Fatalf("Name does not match query sent, %q was provided", msg.Answer[0].Header().Name)
		}

		srvRecord := msg.Answer[0].(*dns.SRV)

		if srvRecord.Priority != 0 || srvRecord.Weight != 0 {
			t.Fatal("Defaults for priority and weight do not equal 0")
		}

		result := SRVRecord{srvRecord.Port, srvRecord.Target}
		if !result.Equal(srv[0]) {
			t.Fatalf("SRV records are not equivalent: received host %q port %d", srvRecord.Target, srvRecord.Port)
		}
	}

	for name := range table {
		server.DeleteSRV(name, "tcp")
	}

	for name := range table {
		msg, err := msgClient(fmt.Sprintf("_%s._tcp.docker.", name), dns.TypeSRV)

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

		if len(msg.Answer) != 0 {
			t.Fatal("Server gave a reply after record has been deleted")
		}
	}
}
<commit_msg>Fix test related to SRV qualification<commit_after>package dnsserver

import (
	"fmt"
	"net"
	"testing"

	"github.com/miekg/dns"
)

// 53 could be in use by a local cache or w/e. 5353 is in use by avahi mDNS on
// my machine. Basically I don't want to have to write a port allocator.
const service = "127.0.0.1:5300"

var server = NewDNSServer("docker")

func init() {
	go func() {
		err := server.Listen(service)
		if err != nil {
			panic(err)
		}
	}()
}

func msgClient(fqdn string, dnsType uint16) (*dns.Msg, error) {
	m := new(dns.Msg)
	m.SetQuestion(fqdn, dnsType)
	return dns.Exchange(m, service)
}

func TestARecordCRUD(t *testing.T) {
	table := map[string]net.IP{
		"test":  net.ParseIP("127.0.0.2"),
		"test2": net.ParseIP("127.0.0.3"),
	}

	// do this in independent parts so both records exist. This tests some
	// collision issues.
	for host, ip := range table {
		server.SetA(host, ip)
	}

	for host, ip := range table {
		msg, err := msgClient(fmt.Sprintf("%s.docker.", host), dns.TypeA)

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

		if len(msg.Answer) != 1 {
			t.Fatalf("Server did not reply with a valid answer.")
		}

		if msg.Answer[0].Header().Ttl != 1 {
			t.Fatalf("TTL was %d instead of 1", msg.Answer[0].Header().Ttl)
		}

		if msg.Answer[0].Header().Rrtype != dns.TypeA {
			t.Fatalf("Expected A record, got a record of type %d instead.", msg.Answer[0].Header().Rrtype)
		}

		if msg.Answer[0].Header().Name != fmt.Sprintf("%s.docker.", host) {
			// If this fails, we probably need to look at miekg/dns as this should
			// not be possible.
			t.Fatalf("Name does not match query sent, %q was provided", msg.Answer[0].Header().Name)
		}

		aRecord := msg.Answer[0].(*dns.A).A
		if !aRecord.Equal(ip) {
			t.Fatalf("IP %q does not match registered IP %q", aRecord, ip)
		}
	}

	for host := range table {
		server.DeleteA(host)
	}

	for host := range table {
		msg, err := msgClient(fmt.Sprintf("%s.docker.", host), dns.TypeA)

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

		if len(msg.Answer) != 0 {
			t.Fatal("Server gave a reply after record has been deleted")
		}
	}
}

func TestSRVRecordCRUD(t *testing.T) {
	table := map[string][]SRVRecord{
		"test":  {{80, "test"}},
		"test2": {{81, "test2"}},
	}

	// do this in independent parts so both records exist. This tests some
	// collision issues.
	for name, srv := range table {
		server.SetSRV(name, "tcp", srv)
	}

	for name, srv := range table {
		msg, err := msgClient(fmt.Sprintf("_%s._tcp.docker.", name), dns.TypeSRV)

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

		if len(msg.Answer) != 1 {
			t.Fatalf("Server did not reply with a valid answer.")
		}

		if msg.Answer[0].Header().Ttl != 1 {
			t.Fatalf("TTL was %d instead of 1", msg.Answer[0].Header().Ttl)
		}

		if msg.Answer[0].Header().Rrtype != dns.TypeSRV {
			t.Fatalf("Expected SRV record, got a record of type %d instead.", msg.Answer[0].Header().Rrtype)
		}

		if msg.Answer[0].Header().Name != fmt.Sprintf("_%s._tcp.docker.", name) {
			// If this fails, we probably need to look at miekg/dns as this should
			// not be possible.
			t.Fatalf("Name does not match query sent, %q was provided", msg.Answer[0].Header().Name)
		}

		srvRecord := msg.Answer[0].(*dns.SRV)

		if srvRecord.Priority != 0 || srvRecord.Weight != 0 {
			t.Fatal("Defaults for priority and weight do not equal 0")
		}

		if srvRecord.Port != srv[0].Port || srvRecord.Target != fmt.Sprintf("%s.docker.", name) {
			t.Fatalf("SRV records are not equivalent: received host %q port %d", srvRecord.Target, srvRecord.Port)
		}
	}

	for name := range table {
		server.DeleteSRV(name, "tcp")
	}

	for name := range table {
		msg, err := msgClient(fmt.Sprintf("_%s._tcp.docker.", name), dns.TypeSRV)

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

		if len(msg.Answer) != 0 {
			t.Fatal("Server gave a reply after record has been deleted")
		}
	}
}
<|endoftext|>