<commit_before>package hayes

//
// Pretend to be a Hayes modem.
//
// References:
// - Hayes command/error documentation:
//    http://www.messagestick.net/modem/hayes_modem.html#Introduction
// - Sounds: https://en.wikipedia.org/wiki/Precise_Tone_Plan
// - RS232: https://en.wikipedia.org/wiki/RS-232
// - Serial Programming: https://en.wikibooks.org/wiki/Serial_Programming
// - Raspberry PI lib: github.com/stianeikeland/go-rpio
//

import (
	"os"
	"os/signal"
	"fmt"
	"time"
	"net"
	"sync"
)

/*
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
char getch(){
    char ch = 0;
    struct termios old = {0};
    fflush(stdout);
    if( tcgetattr(0, &old) < 0 ) perror("tcsetattr()");
    old.c_lflag &= ~ICANON;
    old.c_lflag &= ~ECHO;
    old.c_cc[VMIN] = 1;
    old.c_cc[VTIME] = 0;
    if( tcsetattr(0, TCSANOW, &old) < 0 ) perror("tcsetattr ICANON");
    if( read(0, &ch,1) < 0 ) perror("read()");
    old.c_lflag |= ICANON;
    old.c_lflag |= ECHO;
    if(tcsetattr(0, TCSADRAIN, &old) < 0) perror("tcsetattr ~ICANON");
    return ch;
}
*/
import "C"

////////////////////////////////////////////////////////////////////////////////////

const (
	COMMANDMODE = iota
	DATAMODE
)

const OFFHOOK = false
const ONHOOK = true
const __MAX_RINGS = 15
const __DELAY_MS = 20
const __CONNECT_TIMEOUT = __MAX_RINGS * 6 * time.Second

//Basic modem struct
type Modem struct {
	mode int
	onhook bool
	echo bool
	speakermode int
	volume int
	verbose bool
	quiet bool
	lastcmds []string
	lastdialed string
	rlock sync.RWMutex	// Lock for registers map (r)
	r map[byte]byte
	curreg int
	conn net.Conn
	pins Pins
	leds Pins
	d [10]int
	connect_speed int
}

// Setup/reset modem.  Also ATZ, conveniently.
func (m *Modem) reset() (int) {
	m.onHook()
	m.lowerDSR()
	m.lowerCTS()
	m.lowerRI()

	m.echo = true		// Echo local keypresses
	m.quiet = false		// Modem offers return status
	m.verbose = true	// Text return codes
	m.volume = 1		// moderate volume
	m.speakermode = 1	// on until other modem heard
	m.lastcmds = nil
	m.lastdialed = ""
	m.setupRegs()
	m.setupDebug()

	time.Sleep(250 *time.Millisecond) // Make it look good

	m.raiseDSR()
	m.raiseCTS()		// Ready for DTE to send us data
	return OK
}

// Watch a subset of pins and registers and toggle the LED as apropriate
// Must be a goroutine
func (m *Modem) handlePINs() {
	for {
		if m.readDTR() {
			m.led_TR_on()
		} else {
			if !m.onhook && m.conn != nil {
				// DTE Dropped DTR, hang up the phone if DTR is not
				// reestablished withing S25 * 1/100's of a second
				time.Sleep(time.Duration(m.readReg(REG_DTR_DELAY)) *
					100 * time.Millisecond)
				if m.readDTR() == false && !m.onhook &&
					m.conn != nil {
					m.onHook()
				}
			}
			m.led_TR_off()
		}

		// debug
		if m.d[1] == 2 {
			m.raiseDSR()
			m.raiseCTS()
			m.d[1] = 0
		}
		if m.d[1] == 1 {
			m.lowerDSR()
			m.lowerCTS()
			m.d[1] = 0
		}

		if m.d[2] != 0 {
			m.ledTest(m.d[2])
			m.d[2] = 0
		}

		time.Sleep(250 * time.Millisecond)
	}
}

func (m *Modem) handleModem() {
	// Handle:
	// - passing bytes from the modem to the serial port (stdout for now)
	// - accepting incoming connections (ie, noticing the phone ringing)
	// - other housekeeping tasks (eg, clearing the ring counter)
	//
	// This must be a goroutine.

	// Clear the ring counter if there's been no rings for at least 8 seconds
	last_ring_time := time.Now()
	go func() {
		for range time.Tick(8 * time.Second) {
			if time.Since(last_ring_time) >= 8 * time.Second {
				m.writeReg(REG_RING_COUNT, 0)
			}
		}
	}()

	l, err := net.Listen("tcp", ":20000")
	if err != nil {
		panic(err)
	}
	defer l.Close()

	var zero []byte
	zero = make([]byte, 1)
	zero[0] = 0

	for {
		conn, err := l.Accept()
		if err != nil {
			debugf("l.Accept(): %s\n", err)
			continue
		}

		if !m.onhook {	// "Busy" signal.
			conn.Write([]byte("BUSY\n"))
			conn.Close()
			continue
		}

		for i := 0; i < __MAX_RINGS; i++ {
			last_ring_time = time.Now()
			m.prstatus(RING)
			if !m.onhook { // computer has issued 'ATA'
				m.conn = conn
				conn = nil
				goto answered
			}

			// Simulate the "2-4" pattern for POTS ring signal (2
			// seconds of high voltage ring signal, 4 seconds
			// of silence)

			// Ring for 2s
			d := 0
			m.raiseRI()
			for m.onhook  && d < 2000 {
				if _, err = conn.Write(zero); err != nil {
					goto no_answer
				}
				time.Sleep(__DELAY_MS * time.Millisecond)
				d += __DELAY_MS
				if !m.onhook { // computer has issued 'ATA'
					m.conn = conn
					conn = nil
					goto answered
				}
			}
			m.lowerRI()

			// If Auto Answer if enabled and we've
			// exceeded the configured number of rings to
			// wait before answering, answer the call.  We
			// do this here before the 4s delay as I think
			// it feels more correct.
			if m.readReg(REG_AUTO_ANSWER) > 0 {
				if m.incReg(REG_RING_COUNT) >=
					m.readReg(REG_AUTO_ANSWER) {
					m.answer()
				}
			}

			// Silence for 4s
			d = 0
			for m.onhook && d < 4000 {
				if _, err = conn.Write(zero); err != nil {
					goto no_answer
				}

				time.Sleep(__DELAY_MS * time.Millisecond)
				d += __DELAY_MS
				if !m.onhook { // computer has issued 'ATA'
					m.conn = conn
					conn = nil
					goto answered
				}
			}
		}

	no_answer:
		// At this point we've not answered and have timed out, or the
		// caller hung up before we answered.
		if m.onhook {
			conn.Close()
			m.lowerRI()
			continue
		}

	answered:
		// if we're here, the computer answered, so pass bytes
		// from the remote dialer to the serial port (for now, stdout)
		// as long as we're offhook, we're in DATA MODE and we have
		// valid carrier (m.comm != nil)
		//
		// TODO: Negoitate Telnet behavior -- we're telnetd, pretty much
		// TODO:   character based, no local echo
		// TODO: Accept SSH connections
		// TODO: Blink the RD LED somewhere in here, probably with a
		// TODO:   delay to make it look good.
		// TODO: Read() with a timeout?
		m.writeReg(REG_RING_COUNT, 0)
		m.lowerRI()
		buf := make([]byte, 1)
		for !m.onhook {
			if _, err = m.conn.Read(buf); err != nil {
				debugf("m.conn.Read(): %s", err)
				// carrier lost
				break
			}
			m.led_RD_on()
			if m.mode == DATAMODE {
				fmt.Printf("%s", string(buf)) //  Send to DTE
			}
			m.led_RD_off()
		}

		// If we're here, we lost "carrier" somehow.
		m.led_RD_off()
		m.prstatus(NO_CARRIER)
		m.onHook()
		if m.conn != nil {
			m.conn.Close() // just to be safe?
		}
	}
}

// Catch ^C, reset the HW pins
func (m *Modem) signalHandler() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	s := <-c
	fmt.Println("Got signal:", s)
	m.clearPins()
	os.Exit(0)
}

// Boot the modem
func (m *Modem) PowerOn() {
	m.setupPins()
	m.reset()	      // Setup modem inital state (or reset initial state)

	go m.signalHandler()	// Catch signals in a different thread
	go m.handlePINs()       // Monitor input pins & internal registers
	go m.handleModem()	// Handle in-bound bytes in a seperate goroutine

	// Signal to DTE that we're ready
	m.raiseDSR()
	m.raiseCTS()

	// Tell user we're ready
	m.prstatus(OK)

	// Consume bytes from the serial port and process or send to remote
	// as per m.mode
	var c byte
	var s string
	var lastthree [3]byte
	var out []byte
	var idx int
	var guard_time time.Duration
	var sinceLastChar time.Time

	out = make([]byte, 1)
	for {
		// XXX becuse this is not just a modem program yet, some static
		// key mapping is needed
		c = byte(C.getch())
		if c == 127 {	// ASCII DEL -> ASCII BS
			c = m.readReg(REG_BS_CH)
		}
		// Ignore anything above ASCII 127 or the ASCII escape
		if c > 127 || c == 27 {
			continue
		}
		// end of key mappings

		if m.echo {
			fmt.Printf("%c", c)
			// XXX: handle backspace
			if c == m.readReg(REG_BS_CH) {
				fmt.Printf(" %c", c)
			}
		}

		switch m.mode {
		case COMMANDMODE:
			if c == m.readReg(REG_LF_CH) && s != "" {
				m.command(s)
				s = ""
			}  else if c == m.readReg(REG_BS_CH)  && len(s) > 0 {
				s = s[0:len(s) - 1]
			} else {
				s += string(c)
			}

		case DATAMODE:
			if m.onhook == false && m.conn != nil {
				m.led_SD_on()
				out[0] = c
				m.conn.Write(out)
				time.Sleep(10 *time.Millisecond) // HACK!
				m.led_SD_off()
				// TODO: make sure the LED says on long enough
			}

			// Look for the command escape sequence
			lastthree[idx] = c
			idx = (idx + 1) % 3
			guard_time =
				time.Duration(float64(m.readReg(REG_ESC_CODE_GUARD))				* 0.02) * time.Second

			if lastthree[0] == m.readReg(REG_ESC_CH) &&
				lastthree[1] == m.readReg(REG_ESC_CH) &&
				lastthree[2] == m.readReg(REG_ESC_CH) &&
				time.Since(sinceLastChar) >
				time.Duration(guard_time)  {
				m.mode = COMMANDMODE
				m.prstatus(OK) // signal that we're in command mode
				continue
			}
			if c != '+' {
				sinceLastChar = time.Now()
			}
		}
	}
}
<commit_msg>Telnet negotiation<commit_after>package hayes

//
// Pretend to be a Hayes modem.
//
// References:
// - Hayes command/error documentation:
//    http://www.messagestick.net/modem/hayes_modem.html#Introduction
// - Sounds: https://en.wikipedia.org/wiki/Precise_Tone_Plan
// - RS232: https://en.wikipedia.org/wiki/RS-232
// - Serial Programming: https://en.wikibooks.org/wiki/Serial_Programming
// - Raspberry PI lib: github.com/stianeikeland/go-rpio
//

import (
	"os"
	"os/signal"
	"fmt"
	"time"
	"net"
	"sync"
)

/*
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
char getch(){
    char ch = 0;
    struct termios old = {0};
    fflush(stdout);
    if( tcgetattr(0, &old) < 0 ) perror("tcsetattr()");
    old.c_lflag &= ~ICANON;
    old.c_lflag &= ~ECHO;
    old.c_cc[VMIN] = 1;
    old.c_cc[VTIME] = 0;
    if( tcsetattr(0, TCSANOW, &old) < 0 ) perror("tcsetattr ICANON");
    if( read(0, &ch,1) < 0 ) perror("read()");
    old.c_lflag |= ICANON;
    old.c_lflag |= ECHO;
    if(tcsetattr(0, TCSADRAIN, &old) < 0) perror("tcsetattr ~ICANON");
    return ch;
}
*/
import "C"

////////////////////////////////////////////////////////////////////////////////////

const (
	COMMANDMODE = iota
	DATAMODE
)

const OFFHOOK = false
const ONHOOK = true
const __MAX_RINGS = 15
const __DELAY_MS = 20
const __CONNECT_TIMEOUT = __MAX_RINGS * 6 * time.Second

//Basic modem struct
type Modem struct {
	mode int
	onhook bool
	echo bool
	speakermode int
	volume int
	verbose bool
	quiet bool
	lastcmds []string
	lastdialed string
	rlock sync.RWMutex	// Lock for registers map (r)
	r map[byte]byte
	curreg int
	conn net.Conn
	pins Pins
	leds Pins
	d [10]int
	connect_speed int
}

// Setup/reset modem.  Also ATZ, conveniently.
func (m *Modem) reset() (int) {
	m.onHook()
	m.lowerDSR()
	m.lowerCTS()
	m.lowerRI()

	m.echo = true		// Echo local keypresses
	m.quiet = false		// Modem offers return status
	m.verbose = true	// Text return codes
	m.volume = 1		// moderate volume
	m.speakermode = 1	// on until other modem heard
	m.lastcmds = nil
	m.lastdialed = ""
	m.setupRegs()
	m.setupDebug()

	time.Sleep(250 *time.Millisecond) // Make it look good

	m.raiseDSR()
	m.raiseCTS()		// Ready for DTE to send us data
	return OK
}

// Watch a subset of pins and registers and toggle the LED as apropriate
// Must be a goroutine
func (m *Modem) handlePINs() {
	for {
		if m.readDTR() {
			m.led_TR_on()
		} else {
			if !m.onhook && m.conn != nil {
				// DTE Dropped DTR, hang up the phone if DTR is not
				// reestablished withing S25 * 1/100's of a second
				time.Sleep(time.Duration(m.readReg(REG_DTR_DELAY)) *
					100 * time.Millisecond)
				if m.readDTR() == false && !m.onhook &&
					m.conn != nil {
					m.onHook()
				}
			}
			m.led_TR_off()
		}

		// debug
		if m.d[1] == 2 {
			m.raiseDSR()
			m.raiseCTS()
			m.d[1] = 0
		}
		if m.d[1] == 1 {
			m.lowerDSR()
			m.lowerCTS()
			m.d[1] = 0
		}

		if m.d[2] != 0 {
			m.ledTest(m.d[2])
			m.d[2] = 0
		}

		time.Sleep(250 * time.Millisecond)
	}
}

func (m *Modem) handleModem() {
	// Handle:
	// - passing bytes from the modem to the serial port (stdout for now)
	// - accepting incoming connections (ie, noticing the phone ringing)
	// - other housekeeping tasks (eg, clearing the ring counter)
	//
	// This must be a goroutine.

	// Clear the ring counter if there's been no rings for at least 8 seconds
	last_ring_time := time.Now()
	go func() {
		for range time.Tick(8 * time.Second) {
			if time.Since(last_ring_time) >= 8 * time.Second {
				m.writeReg(REG_RING_COUNT, 0)
			}
		}
	}()

	l, err := net.Listen("tcp", ":20000")
	if err != nil {
		panic(err)
	}
	defer l.Close()

	var zero []byte
	zero = make([]byte, 1)
	zero[0] = 0

	for {
		conn, err := l.Accept()
		if err != nil {
			debugf("l.Accept(): %s\n", err)
			continue
		}

		if !m.onhook {	// "Busy" signal.
			conn.Write([]byte("BUSY\n"))
			conn.Close()
			continue
		}

		// This is a telnet session, negotiate char-at-a-time
		conn.Write([]byte("\377\375\042\377\373\001"))

		for i := 0; i < __MAX_RINGS; i++ {
			last_ring_time = time.Now()
			m.prstatus(RING)
			conn.Write([]byte("Ringing...\n"))
			if !m.onhook { // computer has issued 'ATA'
				m.conn = conn
				conn = nil
				goto answered
			}

			// Simulate the "2-4" pattern for POTS ring signal (2
			// seconds of high voltage ring signal, 4 seconds
			// of silence)

			// Ring for 2s
			d := 0
			m.raiseRI()
			for m.onhook  && d < 2000 {
				if _, err = conn.Write(zero); err != nil {
					goto no_answer
				}
				time.Sleep(__DELAY_MS * time.Millisecond)
				d += __DELAY_MS
				if !m.onhook { // computer has issued 'ATA'
					m.conn = conn
					conn = nil
					goto answered
				}
			}
			m.lowerRI()

			// If Auto Answer if enabled and we've
			// exceeded the configured number of rings to
			// wait before answering, answer the call.  We
			// do this here before the 4s delay as I think
			// it feels more correct.
			if m.readReg(REG_AUTO_ANSWER) > 0 {
				if m.incReg(REG_RING_COUNT) >=
					m.readReg(REG_AUTO_ANSWER) {
					m.answer()
				}
			}

			// Silence for 4s
			d = 0
			for m.onhook && d < 4000 {
				if _, err = conn.Write(zero); err != nil {
					goto no_answer
				}

				time.Sleep(__DELAY_MS * time.Millisecond)
				d += __DELAY_MS
				if !m.onhook { // computer has issued 'ATA'
					m.conn = conn
					conn = nil
					goto answered
				}
			}
		}

	no_answer:
		// At this point we've not answered and have timed out, or the
		// caller hung up before we answered.
		if m.onhook {
			conn.Close()
			m.lowerRI()
			continue
		}

	answered:
		// if we're here, the computer answered, so pass bytes
		// from the remote dialer to the serial port (for now, stdout)
		// as long as we're offhook, we're in DATA MODE and we have
		// valid carrier (m.comm != nil)
		//
		// TODO: Negoitate Telnet behavior -- we're telnetd, pretty much
		// TODO:   character based, no local echo
		// TODO: Accept SSH connections
		// TODO: Blink the RD LED somewhere in here, probably with a
		// TODO:   delay to make it look good.
		// TODO: Read() with a timeout?
		m.writeReg(REG_RING_COUNT, 0)
		m.lowerRI()
		buf := make([]byte, 1)
		for !m.onhook {
			if _, err = m.conn.Read(buf); err != nil {
				debugf("m.conn.Read(): %s", err)
				// carrier lost
				break
			}
			m.led_RD_on()
			if m.mode == DATAMODE {
				fmt.Printf("%s", string(buf)) //  Send to DTE
			}
			m.led_RD_off()
		}

		// If we're here, we lost "carrier" somehow.
		m.led_RD_off()
		m.prstatus(NO_CARRIER)
		m.onHook()
		if m.conn != nil {
			m.conn.Close() // just to be safe?
		}
	}
}

// Catch ^C, reset the HW pins
func (m *Modem) signalHandler() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	s := <-c
	fmt.Println("Got signal:", s)
	m.clearPins()
	os.Exit(0)
}

// Boot the modem
func (m *Modem) PowerOn() {
	m.setupPins()
	m.reset()	      // Setup modem inital state (or reset initial state)

	go m.signalHandler()	// Catch signals in a different thread
	go m.handlePINs()       // Monitor input pins & internal registers
	go m.handleModem()	// Handle in-bound bytes in a seperate goroutine

	// Signal to DTE that we're ready
	m.raiseDSR()
	m.raiseCTS()

	// Tell user we're ready
	m.prstatus(OK)

	// Consume bytes from the serial port and process or send to remote
	// as per m.mode
	var c byte
	var s string
	var lastthree [3]byte
	var out []byte
	var idx int
	var guard_time time.Duration
	var sinceLastChar time.Time

	out = make([]byte, 1)
	for {
		// XXX becuse this is not just a modem program yet, some static
		// key mapping is needed
		c = byte(C.getch())
		if c == 127 {	// ASCII DEL -> ASCII BS
			c = m.readReg(REG_BS_CH)
		}
		// Ignore anything above ASCII 127 or the ASCII escape
		if c > 127 || c == 27 {
			continue
		}
		// end of key mappings

		if m.echo {
			fmt.Printf("%c", c)
			// XXX: handle backspace
			if c == m.readReg(REG_BS_CH) {
				fmt.Printf(" %c", c)
			}
		}

		switch m.mode {
		case COMMANDMODE:
			if c == m.readReg(REG_LF_CH) && s != "" {
				m.command(s)
				s = ""
			}  else if c == m.readReg(REG_BS_CH)  && len(s) > 0 {
				s = s[0:len(s) - 1]
			} else {
				s += string(c)
			}

		case DATAMODE:
			if m.onhook == false && m.conn != nil {
				m.led_SD_on()
				out[0] = c
				m.conn.Write(out)
				time.Sleep(10 *time.Millisecond) // HACK!
				m.led_SD_off()
				// TODO: make sure the LED says on long enough
			}

			// Look for the command escape sequence
			lastthree[idx] = c
			idx = (idx + 1) % 3
			guard_time =
				time.Duration(float64(m.readReg(REG_ESC_CODE_GUARD))				* 0.02) * time.Second

			if lastthree[0] == m.readReg(REG_ESC_CH) &&
				lastthree[1] == m.readReg(REG_ESC_CH) &&
				lastthree[2] == m.readReg(REG_ESC_CH) &&
				time.Since(sinceLastChar) >
				time.Duration(guard_time)  {
				m.mode = COMMANDMODE
				m.prstatus(OK) // signal that we're in command mode
				continue
			}
			if c != '+' {
				sinceLastChar = time.Now()
			}
		}
	}
}
<|endoftext|>


<commit_before>package testflight_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("A job with nested volume mounts", func() {
	BeforeEach(func() {
		setAndUnpausePipeline("fixtures/volume-mounting.yml")
	})

	It("procceds through the plan with input under output mounts", func() {
		watch := fly("trigger-job", "-j", inPipeline("input-under-output"), "-w")
		Expect(watch).To(gexec.Exit(0))
		Expect(watch).To(gbytes.Say("some-resource"))
	})

	It("procceds through the plan with input under input mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-under-input"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("helloworld"))
	})

	It("procceds through the plan having output being mapped to dot and input within", func() {
		sess := fly("trigger-job", "-j", inPipeline("output-with-dot-with-input-within"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("bar"))
	})

	It("procceds through the plan with output under input mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("output-under-input"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("hello"))
	})

	// Pending this test for now, @cirocosta will be looking into it
	XIt("procceds through the plan with input same as output mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-same-output"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("hello"))
	})

	It("procceds through the plan with input and output having the same path but a different name", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-output-same-path-diff-name"), "-w")
		Expect(sess).To(gexec.Exit(0))
	})
})
<commit_msg>Revert "tf: temporary pend failing testflight test"<commit_after>package testflight_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("A job with nested volume mounts", func() {
	BeforeEach(func() {
		setAndUnpausePipeline("fixtures/volume-mounting.yml")
	})

	It("procceds through the plan with input under output mounts", func() {
		watch := fly("trigger-job", "-j", inPipeline("input-under-output"), "-w")
		Expect(watch).To(gexec.Exit(0))
		Expect(watch).To(gbytes.Say("some-resource"))
	})

	It("procceds through the plan with input under input mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-under-input"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("helloworld"))
	})

	It("procceds through the plan having output being mapped to dot and input within", func() {
		sess := fly("trigger-job", "-j", inPipeline("output-with-dot-with-input-within"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("bar"))
	})

	It("procceds through the plan with output under input mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("output-under-input"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("hello"))
	})

	It("procceds through the plan with input same as output mounts", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-same-output"), "-w")
		Expect(sess).To(gexec.Exit(0))
		Expect(sess).To(gbytes.Say("hello"))
	})

	It("procceds through the plan with input and output having the same path but a different name", func() {
		sess := fly("trigger-job", "-j", inPipeline("input-output-same-path-diff-name"), "-w")
		Expect(sess).To(gexec.Exit(0))
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package common

import (
	"context"
	"encoding/json"
	"fmt"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

var (
	// tests which use this appear to all pass within the given time
	generalWatchTimeout = int64(60)
)

var _ = ginkgo.Describe("[sig-node] ConfigMap", func() {
	f := framework.NewDefaultFramework("configmap")

	/*
		Release : v1.9
		Testname: ConfigMap, from environment field
		Description: Create a Pod with an environment variable value set using a value from ConfigMap. A ConfigMap value MUST be accessible in the container environment.
	*/
	framework.ConformanceIt("should be consumable via environment variable [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						Env: []v1.EnvVar{
							{
								Name: "CONFIG_DATA_1",
								ValueFrom: &v1.EnvVarSource{
									ConfigMapKeyRef: &v1.ConfigMapKeySelector{
										LocalObjectReference: v1.LocalObjectReference{
											Name: name,
										},
										Key: "data-1",
									},
								},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"CONFIG_DATA_1=value-1",
		})
	})

	/*
		Release: v1.9
		Testname: ConfigMap, from environment variables
		Description: Create a Pod with a environment source from ConfigMap. All ConfigMap values MUST be available as environment variables in the container.
	*/
	framework.ConformanceIt("should be consumable via the environment [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newEnvFromConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						EnvFrom: []v1.EnvFromSource{
							{
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
							{
								Prefix:       "p_",
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"data_1=value-1", "data_2=value-2", "data_3=value-3",
			"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
		})
	})

	/*
	   Release : v1.14
	   Testname: ConfigMap, with empty-key
	   Description: Attempt to create a ConfigMap with an empty key. The creation MUST fail.
	*/
	framework.ConformanceIt("should fail to create ConfigMap with empty key", func() {
		configMap, err := newConfigMapWithEmptyKey(f)
		framework.ExpectError(err, "created configMap %q with empty key in namespace %q", configMap.Name, f.Namespace.Name)
	})

	ginkgo.It("should update ConfigMap successfully", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		configMap.Data = map[string]string{
			"data": "value",
		}
		ginkgo.By(fmt.Sprintf("Updating configMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(context.TODO(), configMap, metav1.UpdateOptions{})
		framework.ExpectNoError(err, "failed to update ConfigMap")

		configMapFromUpdate, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		ginkgo.By(fmt.Sprintf("Verifying update of ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		framework.ExpectEqual(configMapFromUpdate.Data, configMap.Data)
	})

	ginkgo.It("should run through a ConfigMap lifecycle", func() {
		testNamespaceName := f.Namespace.Name
		testConfigMapName := "test-configmap" + string(uuid.NewUUID())

		ginkgo.By("creating a ConfigMap")
		_, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Create(context.TODO(), &v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: testConfigMapName,
				Labels: map[string]string{
					"test-configmap-static": "true",
				},
			},
			Data: map[string]string{
				"valueName": "value",
			},
		}, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		ginkgo.By("setting a watch for the ConfigMap")
		// setup a watch for the ConfigMap
		resourceWatch, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Watch(context.TODO(), metav1.ListOptions{LabelSelector: "test-configmap-static=true", TimeoutSeconds: &generalWatchTimeout})
		framework.ExpectNoError(err, "Failed to setup watch on newly created ConfigMap")

		resourceWatchChan := resourceWatch.ResultChan()
		ginkgo.By("waiting for the ConfigMap to be added")
		foundWatchEvent := false
		for watchEvent := range resourceWatchChan {
			if watchEvent.Type == watch.Added {
				foundWatchEvent = true
				break
			}
		}
		framework.ExpectEqual(true, foundWatchEvent, "expected to find a watch.Delete event configmap %s", testConfigMapName)

		configMapPatchPayload, err := json.Marshal(v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"test-configmap": "patched",
				},
			},
			Data: map[string]string{
				"valueName": "value1",
			},
		})
		framework.ExpectNoError(err, "failed to marshal patch data")

		ginkgo.By("patching the ConfigMap")
		_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Patch(context.TODO(), testConfigMapName, types.StrategicMergePatchType, []byte(configMapPatchPayload), metav1.PatchOptions{})
		framework.ExpectNoError(err, "failed to patch ConfigMap")
		ginkgo.By("waiting for the ConfigMap to be modified")
		foundWatchEvent = false
		for watchEvent := range resourceWatchChan {
			if watchEvent.Type == watch.Modified {
				foundWatchEvent = true
				break
			}
		}
		framework.ExpectEqual(true, foundWatchEvent, "expected to find a watch.Modified event configmap %s", testConfigMapName)

		ginkgo.By("fetching the ConfigMap")
		configMap, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Get(context.TODO(), testConfigMapName, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		framework.ExpectEqual(configMap.Data["valueName"], "value1", "failed to patch ConfigMap")
		framework.ExpectEqual(configMap.Labels["test-configmap"], "patched", "failed to patch ConfigMap")

		ginkgo.By("listing all ConfigMaps in all namespaces")
		configMapList, err := f.ClientSet.CoreV1().ConfigMaps("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to list ConfigMaps with LabelSelector")
		framework.ExpectNotEqual(len(configMapList.Items), 0, "no ConfigMaps found in ConfigMap list")
		testConfigMapFound := false
		for _, cm := range configMapList.Items {
			if cm.ObjectMeta.Name == testConfigMapName &&
				cm.ObjectMeta.Namespace == testNamespaceName &&
				cm.ObjectMeta.Labels["test-configmap-static"] == "true" &&
				cm.Data["valueName"] == "value1" {
				testConfigMapFound = true
				break
			}
		}
		framework.ExpectEqual(testConfigMapFound, true, "failed to find ConfigMap in list")

		ginkgo.By("deleting the ConfigMap by a collection")
		err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to delete ConfigMap collection with LabelSelector")
		ginkgo.By("waiting for the ConfigMap to be deleted")
		foundWatchEvent = false
		for watchEvent := range resourceWatchChan {
			if watchEvent.Type == watch.Deleted {
				foundWatchEvent = true
				break
			}
		}
		framework.ExpectEqual(true, foundWatchEvent, "expected to find a watch.Deleted event configmap %s", testConfigMapName)
	})
})

func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"data_1": "value-1",
			"data_2": "value-2",
			"data_3": "value-3",
		},
	}
}

func newConfigMapWithEmptyKey(f *framework.Framework) (*v1.ConfigMap, error) {
	name := "configmap-test-emptyKey-" + string(uuid.NewUUID())
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"": "value-1",
		},
	}

	ginkgo.By(fmt.Sprintf("Creating configMap that has name %s", configMap.Name))
	return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
}
<commit_msg>Update to include watch tooling<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package common

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	watchtools "k8s.io/client-go/tools/watch"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

var (
	// tests which use this appear to all pass within the given time
	generalWatchTimeout = int64(60)
)

var _ = ginkgo.Describe("[sig-node] ConfigMap", func() {
	f := framework.NewDefaultFramework("configmap")

	var dc dynamic.Interface

	ginkgo.BeforeEach(func() {
		dc = f.DynamicClient
	})

	/*
		Release : v1.9
		Testname: ConfigMap, from environment field
		Description: Create a Pod with an environment variable value set using a value from ConfigMap. A ConfigMap value MUST be accessible in the container environment.
	*/
	framework.ConformanceIt("should be consumable via environment variable [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						Env: []v1.EnvVar{
							{
								Name: "CONFIG_DATA_1",
								ValueFrom: &v1.EnvVarSource{
									ConfigMapKeyRef: &v1.ConfigMapKeySelector{
										LocalObjectReference: v1.LocalObjectReference{
											Name: name,
										},
										Key: "data-1",
									},
								},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"CONFIG_DATA_1=value-1",
		})
	})

	/*
		Release: v1.9
		Testname: ConfigMap, from environment variables
		Description: Create a Pod with a environment source from ConfigMap. All ConfigMap values MUST be available as environment variables in the container.
	*/
	framework.ConformanceIt("should be consumable via the environment [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newEnvFromConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						EnvFrom: []v1.EnvFromSource{
							{
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
							{
								Prefix:       "p_",
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"data_1=value-1", "data_2=value-2", "data_3=value-3",
			"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
		})
	})

	/*
	   Release : v1.14
	   Testname: ConfigMap, with empty-key
	   Description: Attempt to create a ConfigMap with an empty key. The creation MUST fail.
	*/
	framework.ConformanceIt("should fail to create ConfigMap with empty key", func() {
		configMap, err := newConfigMapWithEmptyKey(f)
		framework.ExpectError(err, "created configMap %q with empty key in namespace %q", configMap.Name, f.Namespace.Name)
	})

	ginkgo.It("should update ConfigMap successfully", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		configMap.Data = map[string]string{
			"data": "value",
		}
		ginkgo.By(fmt.Sprintf("Updating configMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(context.TODO(), configMap, metav1.UpdateOptions{})
		framework.ExpectNoError(err, "failed to update ConfigMap")

		configMapFromUpdate, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		ginkgo.By(fmt.Sprintf("Verifying update of ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		framework.ExpectEqual(configMapFromUpdate.Data, configMap.Data)
	})

	ginkgo.It("should run through a ConfigMap lifecycle", func() {
		testNamespaceName := f.Namespace.Name
		testConfigMapName := "test-configmap" + string(uuid.NewUUID())

		configMapResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
		expectedWatchEvents := []watch.Event{
			{Type: watch.Added},
			{Type: watch.Modified},
			{Type: watch.Deleted},
		}
		testConfigMap := v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: testConfigMapName,
				Labels: map[string]string{
					"test-configmap-static": "true",
				},
			},
			Data: map[string]string{
				"valueName": "value",
			},
		}

		framework.WatchEventSequenceVerifier(context.TODO(), dc, configMapResource, testNamespaceName, testConfigMapName, metav1.ListOptions{LabelSelector: "test-configmap-static=true"}, expectedWatchEvents, func(retryWatcher *watchtools.RetryWatcher) (actualWatchEvents []watch.Event) {
			ginkgo.By("creating a ConfigMap")
			_, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Create(context.TODO(), &testConfigMap, metav1.CreateOptions{})
			framework.ExpectNoError(err, "failed to create ConfigMap")
			eventFound := false
			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
			defer cancel()
			_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
				if watchEvent.Type != watch.Added {
					return false, nil
				}
				actualWatchEvents = append(actualWatchEvents, watchEvent)
				eventFound = true
				return true, nil
			})
			framework.ExpectNoError(err, "Wait until condition with watch events should not return an error")
			framework.ExpectEqual(eventFound, true, "failed to find ConfigMap %v event", watch.Added)

			configMapPatchPayload, err := json.Marshal(v1.ConfigMap{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"test-configmap": "patched",
					},
				},
				Data: map[string]string{
					"valueName": "value1",
				},
			})
			framework.ExpectNoError(err, "failed to marshal patch data")

			ginkgo.By("patching the ConfigMap")
			_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Patch(context.TODO(), testConfigMapName, types.StrategicMergePatchType, []byte(configMapPatchPayload), metav1.PatchOptions{})
			framework.ExpectNoError(err, "failed to patch ConfigMap")
			ginkgo.By("waiting for the ConfigMap to be modified")
			eventFound = false
			ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
			defer cancel()
			_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
				if watchEvent.Type != watch.Modified {
					return false, nil
				}
				actualWatchEvents = append(actualWatchEvents, watchEvent)
				eventFound = true
				return true, nil
			})
			framework.ExpectNoError(err, "Wait until condition with watch events should not return an error")
			framework.ExpectEqual(eventFound, true, "failed to find ConfigMap %v event", watch.Modified)

			ginkgo.By("fetching the ConfigMap")
			configMap, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Get(context.TODO(), testConfigMapName, metav1.GetOptions{})
			framework.ExpectNoError(err, "failed to get ConfigMap")
			framework.ExpectEqual(configMap.Data["valueName"], "value1", "failed to patch ConfigMap")
			framework.ExpectEqual(configMap.Labels["test-configmap"], "patched", "failed to patch ConfigMap")

			ginkgo.By("listing all ConfigMaps in all namespaces")
			configMapList, err := f.ClientSet.CoreV1().ConfigMaps("").List(context.TODO(), metav1.ListOptions{
				LabelSelector: "test-configmap-static=true",
			})
			framework.ExpectNoError(err, "failed to list ConfigMaps with LabelSelector")
			framework.ExpectNotEqual(len(configMapList.Items), 0, "no ConfigMaps found in ConfigMap list")
			testConfigMapFound := false
			for _, cm := range configMapList.Items {
				if cm.ObjectMeta.Name == testConfigMapName &&
					cm.ObjectMeta.Namespace == testNamespaceName &&
					cm.ObjectMeta.Labels["test-configmap-static"] == "true" &&
					cm.Data["valueName"] == "value1" {
					testConfigMapFound = true
					break
				}
			}
			framework.ExpectEqual(testConfigMapFound, true, "failed to find ConfigMap in list")

			ginkgo.By("deleting the ConfigMap by a collection")
			err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{
				LabelSelector: "test-configmap-static=true",
			})
			framework.ExpectNoError(err, "failed to delete ConfigMap collection with LabelSelector")
			ginkgo.By("waiting for the ConfigMap to be deleted")
			eventFound = false
			ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
			defer cancel()
			_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
				if watchEvent.Type != watch.Deleted {
					return false, nil
				}
				actualWatchEvents = append(actualWatchEvents, watchEvent)
				eventFound = true
				return true, nil
			})
			framework.ExpectNoError(err, "Wait until condition with watch events should not return an error")
			framework.ExpectEqual(eventFound, true, "failed to find ConfigMap %v event", watch.Deleted)

			return actualWatchEvents
		})
	})
})

func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"data_1": "value-1",
			"data_2": "value-2",
			"data_3": "value-3",
		},
	}
}

func newConfigMapWithEmptyKey(f *framework.Framework) (*v1.ConfigMap, error) {
	name := "configmap-test-emptyKey-" + string(uuid.NewUUID())
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"": "value-1",
		},
	}

	ginkgo.By(fmt.Sprintf("Creating configMap that has name %s", configMap.Name))
	return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
}
<|endoftext|>


<commit_before>/*
Package session provides configuration for the SDK's service clients.

Sessions can be shared across all service clients that share the same base
configuration.  The Session is built from the SDK's default configuration and
request handlers.

Sessions should be cached when possible, because creating a new Session will
load all configuration values from the environment, and config files each time
the Session is created. Sharing the Session value across all of your service
clients will ensure the configuration is loaded the fewest number of times possible.

Concurrency

Sessions are safe to use concurrently as long as the Session is not being
modified. The SDK will not modify the Session once the Session has been created.
Creating service clients concurrently from a shared Session is safe.

Sessions from Shared Config

Sessions can be created using the method above that will only load the
additional config if the AWS_SDK_LOAD_CONFIG environment variable is set.
Alternatively you can explicitly create a Session with shared config enabled.
To do this you can use NewSessionWithOptions to configure how the Session will
be created. Using the NewSessionWithOptions with SharedConfigState set to
SharedConfigEnable will create the session as if the AWS_SDK_LOAD_CONFIG
environment variable was set.

Creating Sessions

When creating Sessions optional aws.Config values can be passed in that will
override the default, or loaded config values the Session is being created
with. This allows you to provide additional, or case based, configuration
as needed.

By default NewSession will only load credentials from the shared credentials
file (~/.aws/credentials). If the AWS_SDK_LOAD_CONFIG environment variable is
set to a truthy value the Session will be created from the configuration
values from the shared config (~/.aws/config) and shared credentials
(~/.aws/credentials) files. See the section Sessions from Shared Config for
more information.

Create a Session with the default config and request handlers. With credentials
region, and profile loaded from the environment and shared config automatically.
Requires the AWS_PROFILE to be set, or "default" is used.

	// Create Session
	sess := session.Must(session.NewSession())

	// Create a Session with a custom region
	sess := session.Must(session.NewSession(&aws.Config{
		Region: aws.String("us-east-1"),
	}))

	// Create a S3 client instance from a session
	sess := session.Must(session.NewSession())

	svc := s3.New(sess)

Create Session With Option Overrides

In addition to NewSession, Sessions can be created using NewSessionWithOptions.
This func allows you to control and override how the Session will be created
through code instead of being driven by environment variables only.

Use NewSessionWithOptions when you want to provide the config profile, or
override the shared config state (AWS_SDK_LOAD_CONFIG).

	// Equivalent to session.NewSession()
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		// Options
	}))

	// Specify profile to load for the session's config
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		 Profile: "profile_name",
	}))

	// Specify profile for config and region for requests
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		 Config: aws.Config{Region: aws.String("us-east-1")},
		 Profile: "profile_name",
	}))

	// Force enable Shared Config support
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	}))

Adding Handlers

You can add handlers to a session for processing HTTP requests. All service
clients that use the session inherit the handlers. For example, the following
handler logs every request and its payload made by a service client:

	// Create a session, and add additional handlers for all service
	// clients created with the Session to inherit. Adds logging handler.
	sess := session.Must(session.NewSession())

	sess.Handlers.Send.PushFront(func(r *request.Request) {
		// Log every request made and its payload
		logger.Println("Request: %s/%s, Payload: %s",
			r.ClientInfo.ServiceName, r.Operation, r.Params)
	})

Deprecated "New" function

The New session function has been deprecated because it does not provide good
way to return errors that occur when loading the configuration files and values.
Because of this, NewSession was created so errors can be retrieved when
creating a session fails.

Shared Config Fields

By default the SDK will only load the shared credentials file's (~/.aws/credentials)
credentials values, and all other config is provided by the environment variables,
SDK defaults, and user provided aws.Config values.

If the AWS_SDK_LOAD_CONFIG environment variable is set, or SharedConfigEnable
option is used to create the Session the full shared config values will be
loaded. This includes credentials, region, and support for assume role. In
addition the Session will load its configuration from both the shared config
file (~/.aws/config) and shared credentials file (~/.aws/credentials). Both
files have the same format.

If both config files are present the configuration from both files will be
read. The Session will be created from configuration values from the shared
credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config).

Credentials are the values the SDK should use for authenticating requests with
AWS Services. They arfrom a configuration file will need to include both
aws_access_key_id and aws_secret_access_key must be provided together in the
same file to be considered valid. The values will be ignored if not a complete
group. aws_session_token is an optional field that can be provided if both of
the other two fields are also provided.

	aws_access_key_id = AKID
	aws_secret_access_key = SECRET
	aws_session_token = TOKEN

Assume Role values allow you to configure the SDK to assume an IAM role using
a set of credentials provided in a config file via the source_profile field.
Both "role_arn" and "source_profile" are required. The SDK supports assuming
a role with MFA token if the session option AssumeRoleTokenProvider
is set.

	role_arn = arn:aws:iam::<account_number>:role/<role_name>
	source_profile = profile_with_creds
	external_id = 1234
	mfa_serial = <serial or mfa arn>
	role_session_name = session_name

Region is the region the SDK should use for looking up AWS service endpoints
and signing requests.

	region = us-east-1

Assume Role with MFA token

To create a session with support for assuming an IAM role with MFA set the
session option AssumeRoleTokenProvider to a function that will prompt for the
MFA token code when the SDK assumes the role and refreshes the role's credentials.
This allows you to configure the SDK via the shared config to assumea role
with MFA tokens.

In order for the SDK to assume a role with MFA the SharedConfigState
session option must be set to SharedConfigEnable, or AWS_SDK_LOAD_CONFIG
environment variable set.

The shared configuration instructs the SDK to assume an IAM role with MFA
when the mfa_serial configuration field is set in the shared config
(~/.aws/config) or shared credentials (~/.aws/credentials) file.

If mfa_serial is set in the configuration, the SDK will assume the role, and
the AssumeRoleTokenProvider session option is not set an an error will
be returned when creating the session.

    sess := session.Must(session.NewSessionWithOptions(session.Options{
        AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
    }))

    // Create service client value configured for credentials
    // from assumed role.
    svc := s3.New(sess)

To setup assume role outside of a session see the stscrds.AssumeRoleProvider
documentation.

Environment Variables

When a Session is created several environment variables can be set to adjust
how the SDK functions, and what configuration data it loads when creating
Sessions. All environment values are optional, but some values like credentials
require multiple of the values to set or the partial values will be ignored.
All environment variable values are strings unless otherwise noted.

Environment configuration values. If set both Access Key ID and Secret Access
Key must be provided. Session Token and optionally also be provided, but is
not required.

	# Access Key ID
	AWS_ACCESS_KEY_ID=AKID
	AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.

	# Secret Access Key
	AWS_SECRET_ACCESS_KEY=SECRET
	AWS_SECRET_KEY=SECRET=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.

	# Session Token
	AWS_SESSION_TOKEN=TOKEN

Region value will instruct the SDK where to make service API requests to. If is
not provided in the environment the region must be provided before a service
client request is made.

	AWS_REGION=us-east-1

	# AWS_DEFAULT_REGION is only read if AWS_SDK_LOAD_CONFIG is also set,
	# and AWS_REGION is not also set.
	AWS_DEFAULT_REGION=us-east-1

Profile name the SDK should load use when loading shared config from the
configuration files. If not provided "default" will be used as the profile name.

	AWS_PROFILE=my_profile

	# AWS_DEFAULT_PROFILE is only read if AWS_SDK_LOAD_CONFIG is also set,
	# and AWS_PROFILE is not also set.
	AWS_DEFAULT_PROFILE=my_profile

SDK load config instructs the SDK to load the shared config in addition to
shared credentials. This also expands the configuration loaded so the shared
credentials will have parity with the shared config file. This also enables
Region and Profile support for the AWS_DEFAULT_REGION and AWS_DEFAULT_PROFILE
env values as well.

	AWS_SDK_LOAD_CONFIG=1

Shared credentials file path can be set to instruct the SDK to use an alternative
file for the shared credentials. If not set the file will be loaded from
$HOME/.aws/credentials on Linux/Unix based systems, and
%USERPROFILE%\.aws\credentials on Windows.

	AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials

Shared config file path can be set to instruct the SDK to use an alternative
file for the shared config. If not set the file will be loaded from
$HOME/.aws/config on Linux/Unix based systems, and
%USERPROFILE%\.aws\config on Windows.

	AWS_CONFIG_FILE=$HOME/my_shared_config

Path to a custom Credentials Authority (CA) bundle PEM file that the SDK
will use instead of the default system's root CA bundle. Use this only
if you want to replace the CA bundle the SDK uses for TLS requests.

	AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle

Enabling this option will attempt to merge the Transport into the SDK's HTTP
client. If the client's Transport is not a http.Transport an error will be
returned. If the Transport's TLS config is set this option will cause the SDK
to overwrite the Transport's TLS config's  RootCAs value. If the CA bundle file
contains multiple certificates all of them will be loaded.

The Session option CustomCABundle is also available when creating sessions
to also enable this feature. CustomCABundle session option field has priority
over the AWS_CA_BUNDLE environment variable, and will be used if both are set.

Setting a custom HTTPClient in the aws.Config options will override this setting.
To use this option and custom HTTP client, the HTTP client needs to be provided
when creating the session. Not the service client.
*/
package session
<commit_msg>Fixing typo "arfrom" to "are from" (#2017)<commit_after>/*
Package session provides configuration for the SDK's service clients.

Sessions can be shared across all service clients that share the same base
configuration.  The Session is built from the SDK's default configuration and
request handlers.

Sessions should be cached when possible, because creating a new Session will
load all configuration values from the environment, and config files each time
the Session is created. Sharing the Session value across all of your service
clients will ensure the configuration is loaded the fewest number of times possible.

Concurrency

Sessions are safe to use concurrently as long as the Session is not being
modified. The SDK will not modify the Session once the Session has been created.
Creating service clients concurrently from a shared Session is safe.

Sessions from Shared Config

Sessions can be created using the method above that will only load the
additional config if the AWS_SDK_LOAD_CONFIG environment variable is set.
Alternatively you can explicitly create a Session with shared config enabled.
To do this you can use NewSessionWithOptions to configure how the Session will
be created. Using the NewSessionWithOptions with SharedConfigState set to
SharedConfigEnable will create the session as if the AWS_SDK_LOAD_CONFIG
environment variable was set.

Creating Sessions

When creating Sessions optional aws.Config values can be passed in that will
override the default, or loaded config values the Session is being created
with. This allows you to provide additional, or case based, configuration
as needed.

By default NewSession will only load credentials from the shared credentials
file (~/.aws/credentials). If the AWS_SDK_LOAD_CONFIG environment variable is
set to a truthy value the Session will be created from the configuration
values from the shared config (~/.aws/config) and shared credentials
(~/.aws/credentials) files. See the section Sessions from Shared Config for
more information.

Create a Session with the default config and request handlers. With credentials
region, and profile loaded from the environment and shared config automatically.
Requires the AWS_PROFILE to be set, or "default" is used.

	// Create Session
	sess := session.Must(session.NewSession())

	// Create a Session with a custom region
	sess := session.Must(session.NewSession(&aws.Config{
		Region: aws.String("us-east-1"),
	}))

	// Create a S3 client instance from a session
	sess := session.Must(session.NewSession())

	svc := s3.New(sess)

Create Session With Option Overrides

In addition to NewSession, Sessions can be created using NewSessionWithOptions.
This func allows you to control and override how the Session will be created
through code instead of being driven by environment variables only.

Use NewSessionWithOptions when you want to provide the config profile, or
override the shared config state (AWS_SDK_LOAD_CONFIG).

	// Equivalent to session.NewSession()
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		// Options
	}))

	// Specify profile to load for the session's config
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		 Profile: "profile_name",
	}))

	// Specify profile for config and region for requests
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		 Config: aws.Config{Region: aws.String("us-east-1")},
		 Profile: "profile_name",
	}))

	// Force enable Shared Config support
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	}))

Adding Handlers

You can add handlers to a session for processing HTTP requests. All service
clients that use the session inherit the handlers. For example, the following
handler logs every request and its payload made by a service client:

	// Create a session, and add additional handlers for all service
	// clients created with the Session to inherit. Adds logging handler.
	sess := session.Must(session.NewSession())

	sess.Handlers.Send.PushFront(func(r *request.Request) {
		// Log every request made and its payload
		logger.Println("Request: %s/%s, Payload: %s",
			r.ClientInfo.ServiceName, r.Operation, r.Params)
	})

Deprecated "New" function

The New session function has been deprecated because it does not provide good
way to return errors that occur when loading the configuration files and values.
Because of this, NewSession was created so errors can be retrieved when
creating a session fails.

Shared Config Fields

By default the SDK will only load the shared credentials file's (~/.aws/credentials)
credentials values, and all other config is provided by the environment variables,
SDK defaults, and user provided aws.Config values.

If the AWS_SDK_LOAD_CONFIG environment variable is set, or SharedConfigEnable
option is used to create the Session the full shared config values will be
loaded. This includes credentials, region, and support for assume role. In
addition the Session will load its configuration from both the shared config
file (~/.aws/config) and shared credentials file (~/.aws/credentials). Both
files have the same format.

If both config files are present the configuration from both files will be
read. The Session will be created from configuration values from the shared
credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config).

Credentials are the values the SDK should use for authenticating requests with
AWS Services. They are from a configuration file will need to include both
aws_access_key_id and aws_secret_access_key must be provided together in the
same file to be considered valid. The values will be ignored if not a complete
group. aws_session_token is an optional field that can be provided if both of
the other two fields are also provided.

	aws_access_key_id = AKID
	aws_secret_access_key = SECRET
	aws_session_token = TOKEN

Assume Role values allow you to configure the SDK to assume an IAM role using
a set of credentials provided in a config file via the source_profile field.
Both "role_arn" and "source_profile" are required. The SDK supports assuming
a role with MFA token if the session option AssumeRoleTokenProvider
is set.

	role_arn = arn:aws:iam::<account_number>:role/<role_name>
	source_profile = profile_with_creds
	external_id = 1234
	mfa_serial = <serial or mfa arn>
	role_session_name = session_name

Region is the region the SDK should use for looking up AWS service endpoints
and signing requests.

	region = us-east-1

Assume Role with MFA token

To create a session with support for assuming an IAM role with MFA set the
session option AssumeRoleTokenProvider to a function that will prompt for the
MFA token code when the SDK assumes the role and refreshes the role's credentials.
This allows you to configure the SDK via the shared config to assumea role
with MFA tokens.

In order for the SDK to assume a role with MFA the SharedConfigState
session option must be set to SharedConfigEnable, or AWS_SDK_LOAD_CONFIG
environment variable set.

The shared configuration instructs the SDK to assume an IAM role with MFA
when the mfa_serial configuration field is set in the shared config
(~/.aws/config) or shared credentials (~/.aws/credentials) file.

If mfa_serial is set in the configuration, the SDK will assume the role, and
the AssumeRoleTokenProvider session option is not set an an error will
be returned when creating the session.

    sess := session.Must(session.NewSessionWithOptions(session.Options{
        AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
    }))

    // Create service client value configured for credentials
    // from assumed role.
    svc := s3.New(sess)

To setup assume role outside of a session see the stscrds.AssumeRoleProvider
documentation.

Environment Variables

When a Session is created several environment variables can be set to adjust
how the SDK functions, and what configuration data it loads when creating
Sessions. All environment values are optional, but some values like credentials
require multiple of the values to set or the partial values will be ignored.
All environment variable values are strings unless otherwise noted.

Environment configuration values. If set both Access Key ID and Secret Access
Key must be provided. Session Token and optionally also be provided, but is
not required.

	# Access Key ID
	AWS_ACCESS_KEY_ID=AKID
	AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.

	# Secret Access Key
	AWS_SECRET_ACCESS_KEY=SECRET
	AWS_SECRET_KEY=SECRET=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.

	# Session Token
	AWS_SESSION_TOKEN=TOKEN

Region value will instruct the SDK where to make service API requests to. If is
not provided in the environment the region must be provided before a service
client request is made.

	AWS_REGION=us-east-1

	# AWS_DEFAULT_REGION is only read if AWS_SDK_LOAD_CONFIG is also set,
	# and AWS_REGION is not also set.
	AWS_DEFAULT_REGION=us-east-1

Profile name the SDK should load use when loading shared config from the
configuration files. If not provided "default" will be used as the profile name.

	AWS_PROFILE=my_profile

	# AWS_DEFAULT_PROFILE is only read if AWS_SDK_LOAD_CONFIG is also set,
	# and AWS_PROFILE is not also set.
	AWS_DEFAULT_PROFILE=my_profile

SDK load config instructs the SDK to load the shared config in addition to
shared credentials. This also expands the configuration loaded so the shared
credentials will have parity with the shared config file. This also enables
Region and Profile support for the AWS_DEFAULT_REGION and AWS_DEFAULT_PROFILE
env values as well.

	AWS_SDK_LOAD_CONFIG=1

Shared credentials file path can be set to instruct the SDK to use an alternative
file for the shared credentials. If not set the file will be loaded from
$HOME/.aws/credentials on Linux/Unix based systems, and
%USERPROFILE%\.aws\credentials on Windows.

	AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials

Shared config file path can be set to instruct the SDK to use an alternative
file for the shared config. If not set the file will be loaded from
$HOME/.aws/config on Linux/Unix based systems, and
%USERPROFILE%\.aws\config on Windows.

	AWS_CONFIG_FILE=$HOME/my_shared_config

Path to a custom Credentials Authority (CA) bundle PEM file that the SDK
will use instead of the default system's root CA bundle. Use this only
if you want to replace the CA bundle the SDK uses for TLS requests.

	AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle

Enabling this option will attempt to merge the Transport into the SDK's HTTP
client. If the client's Transport is not a http.Transport an error will be
returned. If the Transport's TLS config is set this option will cause the SDK
to overwrite the Transport's TLS config's  RootCAs value. If the CA bundle file
contains multiple certificates all of them will be loaded.

The Session option CustomCABundle is also available when creating sessions
to also enable this feature. CustomCABundle session option field has priority
over the AWS_CA_BUNDLE environment variable, and will be used if both are set.

Setting a custom HTTPClient in the aws.Config options will override this setting.
To use this option and custom HTTP client, the HTTP client needs to be provided
when creating the session. Not the service client.
*/
package session
<|endoftext|>


<commit_before>// Copyright 2009 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 gosym

import (
	"debug/elf"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var (
	pclineTempDir    string
	pclinetestBinary string
)

func dotest(self bool) bool {
	// For now, only works on amd64 platforms.
	if runtime.GOARCH != "amd64" {
		return false
	}
	// Self test reads test binary; only works on Linux.
	if self && runtime.GOOS != "linux" {
		return false
	}
	if pclinetestBinary != "" {
		return true
	}
	var err error
	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
	if err != nil {
		panic(err)
	}
	if strings.Contains(pclineTempDir, " ") {
		panic("unexpected space in tempdir")
	}
	// This command builds pclinetest from pclinetest.asm;
	// the resulting binary looks like it was built from pclinetest.s,
	// but we have renamed it to keep it away from the go tool.
	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
	pclinetestBinary = "pclinetest"
	command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
		pclinetestBinary, pclinetestBinary, pclinetestBinary)
	cmd := exec.Command("sh", "-c", command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	return true
}

func endtest() {
	if pclineTempDir != "" {
		os.RemoveAll(pclineTempDir)
		pclineTempDir = ""
		pclinetestBinary = ""
	}
}

func getTable(t *testing.T) *Table {
	f, tab := crack(os.Args[0], t)
	f.Close()
	return tab
}

func crack(file string, t *testing.T) (*elf.File, *Table) {
	// Open self
	f, err := elf.Open(file)
	if err != nil {
		t.Fatal(err)
	}
	return parse(file, f, t)
}

func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
	symdat, err := f.Section(".gosymtab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gosymtab: %v", file, err)
	}
	pclndat, err := f.Section(".gopclntab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gopclntab: %v", file, err)
	}

	pcln := NewLineTable(pclndat, f.Section(".text").Addr)
	tab, err := NewTable(symdat, pcln)
	if err != nil {
		f.Close()
		t.Fatalf("parsing %s gosymtab: %v", file, err)
	}

	return f, tab
}

var goarch = os.Getenv("O")

func TestLineFromAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	// Find the sym package
	pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
	if pkg == nil {
		t.Fatalf("nil pkg")
	}

	// Walk every absolute line and ensure that we hit every
	// source line monotonically
	lastline := make(map[string]int)
	final := -1
	for i := 0; i < 10000; i++ {
		path, line := pkg.lineFromAline(i)
		// Check for end of object
		if path == "" {
			if final == -1 {
				final = i - 1
			}
			continue
		} else if final != -1 {
			t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
		}
		// It's okay to see files multiple times (e.g., sys.a)
		if line == 1 {
			lastline[path] = 1
			continue
		}
		// Check that the is the next line in path
		ll, ok := lastline[path]
		if !ok {
			t.Errorf("file %s starts on line %d", path, line)
		} else if line != ll+1 {
			t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
		}
		lastline[path] = line
	}
	if final == -1 {
		t.Errorf("never reached end of object")
	}
}

func TestLineAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	for _, o := range tab.Files {
		// A source file can appear multiple times in a
		// object.  alineFromLine will always return alines in
		// the first file, so track which lines we've seen.
		found := make(map[string]int)
		for i := 0; i < 1000; i++ {
			path, line := o.lineFromAline(i)
			if path == "" {
				break
			}

			// cgo files are full of 'Z' symbols, which we don't handle
			if len(path) > 4 && path[len(path)-4:] == ".cgo" {
				continue
			}

			if minline, ok := found[path]; path != "" && ok {
				if minline >= line {
					// We've already covered this file
					continue
				}
			}
			found[path] = line

			a, err := o.alineFromLine(path, line)
			if err != nil {
				t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err)
			} else if a != i {
				t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a)
			}
		}
	}
}

func TestPCLine(t *testing.T) {
	if !dotest(false) {
		return
	}
	defer endtest()

	f, tab := crack(pclinetestBinary, t)
	text := f.Section(".text")
	textdat, err := text.Data()
	if err != nil {
		t.Fatalf("reading .text: %v", err)
	}

	// Test PCToLine
	sym := tab.LookupFunc("linefrompc")
	wantLine := 0
	for pc := sym.Entry; pc < sym.End; pc++ {
		off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
		file, line, fn := tab.PCToLine(pc)
		if fn == nil {
			t.Errorf("failed to get line of PC %#x", pc)
		} else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
			t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
		}
	}

	// Test LineToPC
	sym = tab.LookupFunc("pcfromline")
	lookupline := -1
	wantLine = 0
	off := uint64(0) // TODO(rsc): should not need off; bug in 8g
	for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
		file, line, fn := tab.PCToLine(pc)
		off = pc - text.Addr
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		if line != wantLine {
			t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
			off = pc + 1 - text.Addr
			continue
		}
		if lookupline == -1 {
			lookupline = line
		}
		for ; lookupline <= line; lookupline++ {
			pc2, fn2, err := tab.LineToPC(file, lookupline)
			if lookupline != line {
				// Should be nothing on this line
				if err == nil {
					t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
				}
			} else if err != nil {
				t.Errorf("failed to get PC of line %d: %s", lookupline, err)
			} else if pc != pc2 {
				t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
			}
		}
		off = pc + 1 - text.Addr
	}
}
<commit_msg>debug/gosym: avoid test failure on Windows<commit_after>// Copyright 2009 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 gosym

import (
	"debug/elf"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var (
	pclineTempDir    string
	pclinetestBinary string
)

func dotest(self bool) bool {
	// For now, only works on amd64 platforms.
	if runtime.GOARCH != "amd64" {
		return false
	}
	// Self test reads test binary; only works on Linux.
	if self && runtime.GOOS != "linux" {
		return false
	}
	// Command below expects "sh", so Unix.
	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		return false
	}
	if pclinetestBinary != "" {
		return true
	}
	var err error
	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
	if err != nil {
		panic(err)
	}
	if strings.Contains(pclineTempDir, " ") {
		panic("unexpected space in tempdir")
	}
	// This command builds pclinetest from pclinetest.asm;
	// the resulting binary looks like it was built from pclinetest.s,
	// but we have renamed it to keep it away from the go tool.
	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
	pclinetestBinary = "pclinetest"
	command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
		pclinetestBinary, pclinetestBinary, pclinetestBinary)
	cmd := exec.Command("sh", "-c", command)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	return true
}

func endtest() {
	if pclineTempDir != "" {
		os.RemoveAll(pclineTempDir)
		pclineTempDir = ""
		pclinetestBinary = ""
	}
}

func getTable(t *testing.T) *Table {
	f, tab := crack(os.Args[0], t)
	f.Close()
	return tab
}

func crack(file string, t *testing.T) (*elf.File, *Table) {
	// Open self
	f, err := elf.Open(file)
	if err != nil {
		t.Fatal(err)
	}
	return parse(file, f, t)
}

func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
	symdat, err := f.Section(".gosymtab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gosymtab: %v", file, err)
	}
	pclndat, err := f.Section(".gopclntab").Data()
	if err != nil {
		f.Close()
		t.Fatalf("reading %s gopclntab: %v", file, err)
	}

	pcln := NewLineTable(pclndat, f.Section(".text").Addr)
	tab, err := NewTable(symdat, pcln)
	if err != nil {
		f.Close()
		t.Fatalf("parsing %s gosymtab: %v", file, err)
	}

	return f, tab
}

var goarch = os.Getenv("O")

func TestLineFromAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	// Find the sym package
	pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
	if pkg == nil {
		t.Fatalf("nil pkg")
	}

	// Walk every absolute line and ensure that we hit every
	// source line monotonically
	lastline := make(map[string]int)
	final := -1
	for i := 0; i < 10000; i++ {
		path, line := pkg.lineFromAline(i)
		// Check for end of object
		if path == "" {
			if final == -1 {
				final = i - 1
			}
			continue
		} else if final != -1 {
			t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
		}
		// It's okay to see files multiple times (e.g., sys.a)
		if line == 1 {
			lastline[path] = 1
			continue
		}
		// Check that the is the next line in path
		ll, ok := lastline[path]
		if !ok {
			t.Errorf("file %s starts on line %d", path, line)
		} else if line != ll+1 {
			t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
		}
		lastline[path] = line
	}
	if final == -1 {
		t.Errorf("never reached end of object")
	}
}

func TestLineAline(t *testing.T) {
	if !dotest(true) {
		return
	}
	defer endtest()

	tab := getTable(t)
	if tab.go12line != nil {
		// aline's don't exist in the Go 1.2 table.
		t.Skip("not relevant to Go 1.2 symbol table")
	}

	for _, o := range tab.Files {
		// A source file can appear multiple times in a
		// object.  alineFromLine will always return alines in
		// the first file, so track which lines we've seen.
		found := make(map[string]int)
		for i := 0; i < 1000; i++ {
			path, line := o.lineFromAline(i)
			if path == "" {
				break
			}

			// cgo files are full of 'Z' symbols, which we don't handle
			if len(path) > 4 && path[len(path)-4:] == ".cgo" {
				continue
			}

			if minline, ok := found[path]; path != "" && ok {
				if minline >= line {
					// We've already covered this file
					continue
				}
			}
			found[path] = line

			a, err := o.alineFromLine(path, line)
			if err != nil {
				t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err)
			} else if a != i {
				t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a)
			}
		}
	}
}

func TestPCLine(t *testing.T) {
	if !dotest(false) {
		return
	}
	defer endtest()

	f, tab := crack(pclinetestBinary, t)
	text := f.Section(".text")
	textdat, err := text.Data()
	if err != nil {
		t.Fatalf("reading .text: %v", err)
	}

	// Test PCToLine
	sym := tab.LookupFunc("linefrompc")
	wantLine := 0
	for pc := sym.Entry; pc < sym.End; pc++ {
		off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
		file, line, fn := tab.PCToLine(pc)
		if fn == nil {
			t.Errorf("failed to get line of PC %#x", pc)
		} else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
			t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
		}
	}

	// Test LineToPC
	sym = tab.LookupFunc("pcfromline")
	lookupline := -1
	wantLine = 0
	off := uint64(0) // TODO(rsc): should not need off; bug in 8g
	for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
		file, line, fn := tab.PCToLine(pc)
		off = pc - text.Addr
		if textdat[off] == 255 {
			break
		}
		wantLine += int(textdat[off])
		if line != wantLine {
			t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
			off = pc + 1 - text.Addr
			continue
		}
		if lookupline == -1 {
			lookupline = line
		}
		for ; lookupline <= line; lookupline++ {
			pc2, fn2, err := tab.LineToPC(file, lookupline)
			if lookupline != line {
				// Should be nothing on this line
				if err == nil {
					t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
				}
			} else if err != nil {
				t.Errorf("failed to get PC of line %d: %s", lookupline, err)
			} else if pc != pc2 {
				t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
			}
		}
		off = pc + 1 - text.Addr
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 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 websocket

import (
	"bufio"
	"bytes"
	"fmt"
	"http"
	"http/httptest"
	"io"
	"log"
	"net"
	"sync"
	"testing"
)

var serverAddr string
var once sync.Once

func echoServer(ws *Conn) { io.Copy(ws, ws) }

func startServer() {
	http.Handle("/echo", Handler(echoServer))
	http.Handle("/echoDraft75", Draft75Handler(echoServer))
	server := httptest.NewServer(nil)
	serverAddr = server.Listener.Addr().String()
	log.Print("Test WebSocket server listening on ", serverAddr)
}

// Test the getChallengeResponse function with values from section
// 5.1 of the specification steps 18, 26, and 43 from
// http://www.whatwg.org/specs/web-socket-protocol/
func TestChallenge(t *testing.T) {
	var part1 uint32 = 777007543
	var part2 uint32 = 114997259
	key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58}
	expected := []byte("0st3Rl&q-2ZU^weu")

	response, err := getChallengeResponse(part1, part2, key3)
	if err != nil {
		t.Errorf("getChallengeResponse: returned error %v", err)
		return
	}
	if !bytes.Equal(expected, response) {
		t.Errorf("getChallengeResponse: expected %q got %q", expected, response)
	}
}

func TestEcho(t *testing.T) {
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake error: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: %v", err)
	}
	var actual_msg = make([]byte, 512)
	n, err := ws.Read(actual_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	actual_msg = actual_msg[0:n]
	if !bytes.Equal(msg, actual_msg) {
		t.Errorf("Echo: expected %q got %q", msg, actual_msg)
	}
	ws.Close()
}

func TestEchoDraft75(t *testing.T) {
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echoDraft75", "localhost", "http://localhost",
		"ws://localhost/echoDraft75", "", client, draft75handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: error %v", err)
	}
	var actual_msg = make([]byte, 512)
	n, err := ws.Read(actual_msg)
	if err != nil {
		t.Errorf("Read: error %v", err)
	}
	actual_msg = actual_msg[0:n]
	if !bytes.Equal(msg, actual_msg) {
		t.Errorf("Echo: expected %q got %q", msg, actual_msg)
	}
	ws.Close()
}

func TestWithQuery(t *testing.T) {
	once.Do(startServer)

	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}

	ws, err := newClient("/echo?q=v", "localhost", "http://localhost",
		"ws://localhost/echo?q=v", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}
	ws.Close()
}

func TestWithProtocol(t *testing.T) {
	once.Do(startServer)

	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}

	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "test", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}
	ws.Close()
}

func TestHTTP(t *testing.T) {
	once.Do(startServer)

	// If the client did not send a handshake that matches the protocol
	// specification, the server should abort the WebSocket connection.
	_, _, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr))
	if err == nil {
		t.Error("Get: unexpected success")
		return
	}
	urlerr, ok := err.(*http.URLError)
	if !ok {
		t.Errorf("Get: not URLError %#v", err)
		return
	}
	if urlerr.Error != io.ErrUnexpectedEOF {
		t.Errorf("Get: error %#v", err)
		return
	}
}

func TestHTTPDraft75(t *testing.T) {
	once.Do(startServer)

	r, _, err := http.Get(fmt.Sprintf("http://%s/echoDraft75", serverAddr))
	if err != nil {
		t.Errorf("Get: error %#v", err)
		return
	}
	if r.StatusCode != http.StatusBadRequest {
		t.Errorf("Get: got status %d", r.StatusCode)
	}
}

func TestTrailingSpaces(t *testing.T) {
	// http://code.google.com/p/go/issues/detail?id=955
	// The last runs of this create keys with trailing spaces that should not be
	// generated by the client.
	once.Do(startServer)
	for i := 0; i < 30; i++ {
		// body
		_, err := Dial(fmt.Sprintf("ws://%s/echo", serverAddr), "",
			"http://localhost/")
		if err != nil {
			panic("Dial failed: " + err.String())
		}
	}
}

func TestSmallBuffer(t *testing.T) {
	// http://code.google.com/p/go/issues/detail?id=1145
	// Read should be able to handle reading a fragment of a frame.
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake error: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: %v", err)
	}
	var small_msg = make([]byte, 8)
	n, err := ws.Read(small_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(msg[:len(small_msg)], small_msg) {
		t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg)
	}
	var second_msg = make([]byte, len(msg))
	n, err = ws.Read(second_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	second_msg = second_msg[0:n]
	if !bytes.Equal(msg[len(small_msg):], second_msg) {
		t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg)
	}
	ws.Close()

}

func testSkipLengthFrame(t *testing.T) {
	b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
	buf := bytes.NewBuffer(b)
	br := bufio.NewReader(buf)
	bw := bufio.NewWriter(buf)
	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
	msg := make([]byte, 5)
	n, err := ws.Read(msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(b[4:8], msg[0:n]) {
		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
	}
}

func testSkipNoUTF8Frame(t *testing.T) {
	b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
	buf := bytes.NewBuffer(b)
	br := bufio.NewReader(buf)
	bw := bufio.NewWriter(buf)
	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
	msg := make([]byte, 5)
	n, err := ws.Read(msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(b[4:8], msg[0:n]) {
		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
	}
}
<commit_msg>websocket: fix socket leak in test<commit_after>// Copyright 2009 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 websocket

import (
	"bufio"
	"bytes"
	"fmt"
	"http"
	"http/httptest"
	"io"
	"log"
	"net"
	"sync"
	"testing"
)

var serverAddr string
var once sync.Once

func echoServer(ws *Conn) { io.Copy(ws, ws) }

func startServer() {
	http.Handle("/echo", Handler(echoServer))
	http.Handle("/echoDraft75", Draft75Handler(echoServer))
	server := httptest.NewServer(nil)
	serverAddr = server.Listener.Addr().String()
	log.Print("Test WebSocket server listening on ", serverAddr)
}

// Test the getChallengeResponse function with values from section
// 5.1 of the specification steps 18, 26, and 43 from
// http://www.whatwg.org/specs/web-socket-protocol/
func TestChallenge(t *testing.T) {
	var part1 uint32 = 777007543
	var part2 uint32 = 114997259
	key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58}
	expected := []byte("0st3Rl&q-2ZU^weu")

	response, err := getChallengeResponse(part1, part2, key3)
	if err != nil {
		t.Errorf("getChallengeResponse: returned error %v", err)
		return
	}
	if !bytes.Equal(expected, response) {
		t.Errorf("getChallengeResponse: expected %q got %q", expected, response)
	}
}

func TestEcho(t *testing.T) {
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake error: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: %v", err)
	}
	var actual_msg = make([]byte, 512)
	n, err := ws.Read(actual_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	actual_msg = actual_msg[0:n]
	if !bytes.Equal(msg, actual_msg) {
		t.Errorf("Echo: expected %q got %q", msg, actual_msg)
	}
	ws.Close()
}

func TestEchoDraft75(t *testing.T) {
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echoDraft75", "localhost", "http://localhost",
		"ws://localhost/echoDraft75", "", client, draft75handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: error %v", err)
	}
	var actual_msg = make([]byte, 512)
	n, err := ws.Read(actual_msg)
	if err != nil {
		t.Errorf("Read: error %v", err)
	}
	actual_msg = actual_msg[0:n]
	if !bytes.Equal(msg, actual_msg) {
		t.Errorf("Echo: expected %q got %q", msg, actual_msg)
	}
	ws.Close()
}

func TestWithQuery(t *testing.T) {
	once.Do(startServer)

	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}

	ws, err := newClient("/echo?q=v", "localhost", "http://localhost",
		"ws://localhost/echo?q=v", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}
	ws.Close()
}

func TestWithProtocol(t *testing.T) {
	once.Do(startServer)

	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}

	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "test", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake: %v", err)
		return
	}
	ws.Close()
}

func TestHTTP(t *testing.T) {
	once.Do(startServer)

	// If the client did not send a handshake that matches the protocol
	// specification, the server should abort the WebSocket connection.
	_, _, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr))
	if err == nil {
		t.Error("Get: unexpected success")
		return
	}
	urlerr, ok := err.(*http.URLError)
	if !ok {
		t.Errorf("Get: not URLError %#v", err)
		return
	}
	if urlerr.Error != io.ErrUnexpectedEOF {
		t.Errorf("Get: error %#v", err)
		return
	}
}

func TestHTTPDraft75(t *testing.T) {
	once.Do(startServer)

	r, _, err := http.Get(fmt.Sprintf("http://%s/echoDraft75", serverAddr))
	if err != nil {
		t.Errorf("Get: error %#v", err)
		return
	}
	if r.StatusCode != http.StatusBadRequest {
		t.Errorf("Get: got status %d", r.StatusCode)
	}
}

func TestTrailingSpaces(t *testing.T) {
	// http://code.google.com/p/go/issues/detail?id=955
	// The last runs of this create keys with trailing spaces that should not be
	// generated by the client.
	once.Do(startServer)
	for i := 0; i < 30; i++ {
		// body
		ws, err := Dial(fmt.Sprintf("ws://%s/echo", serverAddr), "", "http://localhost/")
		if err != nil {
			t.Error("Dial failed:", err.String())
			break
		}
		ws.Close()
	}
}

func TestSmallBuffer(t *testing.T) {
	// http://code.google.com/p/go/issues/detail?id=1145
	// Read should be able to handle reading a fragment of a frame.
	once.Do(startServer)

	// websocket.Dial()
	client, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatal("dialing", err)
	}
	ws, err := newClient("/echo", "localhost", "http://localhost",
		"ws://localhost/echo", "", client, handshake)
	if err != nil {
		t.Errorf("WebSocket handshake error: %v", err)
		return
	}

	msg := []byte("hello, world\n")
	if _, err := ws.Write(msg); err != nil {
		t.Errorf("Write: %v", err)
	}
	var small_msg = make([]byte, 8)
	n, err := ws.Read(small_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(msg[:len(small_msg)], small_msg) {
		t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg)
	}
	var second_msg = make([]byte, len(msg))
	n, err = ws.Read(second_msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	second_msg = second_msg[0:n]
	if !bytes.Equal(msg[len(small_msg):], second_msg) {
		t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg)
	}
	ws.Close()

}

func testSkipLengthFrame(t *testing.T) {
	b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
	buf := bytes.NewBuffer(b)
	br := bufio.NewReader(buf)
	bw := bufio.NewWriter(buf)
	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
	msg := make([]byte, 5)
	n, err := ws.Read(msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(b[4:8], msg[0:n]) {
		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
	}
}

func testSkipNoUTF8Frame(t *testing.T) {
	b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'}
	buf := bytes.NewBuffer(b)
	br := bufio.NewReader(buf)
	bw := bufio.NewWriter(buf)
	ws := newConn("http://127.0.0.1/", "ws://127.0.0.1/", "", bufio.NewReadWriter(br, bw), nil)
	msg := make([]byte, 5)
	n, err := ws.Read(msg)
	if err != nil {
		t.Errorf("Read: %v", err)
	}
	if !bytes.Equal(b[4:8], msg[0:n]) {
		t.Errorf("Read: expected %q got %q", msg[4:8], msg[0:n])
	}
}
<|endoftext|>