<commit_before>package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/connections"
)

const URLRouteCreateGame = "/games"

const MethodCreateGame = http.MethodPost

const (
	postFieldConnectionLimit = "limit"
	postFieldMapWidth        = "width"
	postFieldMapHeight       = "height"
)

type responseCreateGameHandler struct {
	ID     int   `json:"id"`
	Limit  int   `json:"limit"`
	Width  uint8 `json:"width"`
	Height uint8 `json:"height"`
}

type createGameHandler struct {
	logger       logrus.FieldLogger
	groupManager *connections.ConnectionGroupManager
}

type ErrCreateGameHandler string

func (e ErrCreateGameHandler) Error() string {
	return "create game handler error: " + string(e)
}

func NewCreateGameHandler(logger logrus.FieldLogger, groupManager *connections.ConnectionGroupManager) http.Handler {
	return &createGameHandler{
		logger:       logger,
		groupManager: groupManager,
	}
}

func (h *createGameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	connectionLimit, err := strconv.Atoi(r.PostFormValue(postFieldConnectionLimit))
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if connectionLimit <= 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid connection limit"), connectionLimit)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	mapWidth, err := strconv.ParseUint(r.PostFormValue(postFieldMapWidth), 10, 8)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if mapWidth == 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid map width"), mapWidth)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	mapHeight, err := strconv.ParseUint(r.PostFormValue(postFieldMapHeight), 10, 8)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if mapHeight == 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid map height"), mapHeight)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	h.logger.WithFields(logrus.Fields{
		"width":            mapWidth,
		"height":           mapHeight,
		"connection_limit": connectionLimit,
	}).Debug("create game group")

	group, err := connections.NewConnectionGroup(h.logger, connectionLimit, uint8(mapWidth), uint8(mapHeight))
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	id, err := h.groupManager.Add(group)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))

		switch err {
		case connections.ErrGroupLimitReached:
			http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
		case connections.ErrConnsLimitReached:
			http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
		default:
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}

	h.logger.Info("start group")
	group.Start()

	h.logger.WithField("group_id", id).Infoln("created group")

	w.WriteHeader(http.StatusCreated)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	err = json.NewEncoder(w).Encode(responseCreateGameHandler{
		ID:     id,
		Limit:  group.GetLimit(),
		Width:  uint8(mapWidth),
		Height: uint8(mapHeight),
	})
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
}
<commit_msg>Create JSON error response in createGameHandler<commit_after>package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/connections"
)

const URLRouteCreateGame = "/games"

const MethodCreateGame = http.MethodPost

const (
	postFieldConnectionLimit = "limit"
	postFieldMapWidth        = "width"
	postFieldMapHeight       = "height"
)

type responseCreateGameHandler struct {
	ID     int   `json:"id"`
	Limit  int   `json:"limit"`
	Width  uint8 `json:"width"`
	Height uint8 `json:"height"`
}

type responseCreateGameHandlerError struct {
	Code int    `json:"code"`
	Text string `json:"text"`
}

type createGameHandler struct {
	logger       logrus.FieldLogger
	groupManager *connections.ConnectionGroupManager
}

type ErrCreateGameHandler string

func (e ErrCreateGameHandler) Error() string {
	return "create game handler error: " + string(e)
}

func NewCreateGameHandler(logger logrus.FieldLogger, groupManager *connections.ConnectionGroupManager) http.Handler {
	return &createGameHandler{
		logger:       logger,
		groupManager: groupManager,
	}
}

func (h *createGameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	connectionLimit, err := strconv.Atoi(r.PostFormValue(postFieldConnectionLimit))
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid limit",
		})
		return
	}
	if connectionLimit <= 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid connection limit"), connectionLimit)
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid limit",
		})
		return
	}

	mapWidth, err := strconv.ParseUint(r.PostFormValue(postFieldMapWidth), 10, 8)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid width",
		})
		return
	}
	if mapWidth == 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid map width"), mapWidth)
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid width",
		})
		return
	}

	mapHeight, err := strconv.ParseUint(r.PostFormValue(postFieldMapHeight), 10, 8)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid height",
		})
		return
	}
	if mapHeight == 0 {
		h.logger.Warnln(ErrCreateGameHandler("invalid map height"), mapHeight)
		h.writeResponseJSON(w, http.StatusBadRequest, &responseCreateGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid height",
		})
		return
	}

	h.logger.WithFields(logrus.Fields{
		"width":            mapWidth,
		"height":           mapHeight,
		"connection_limit": connectionLimit,
	}).Debug("create game group")

	group, err := connections.NewConnectionGroup(h.logger, connectionLimit, uint8(mapWidth), uint8(mapHeight))
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
		h.writeResponseJSON(w, http.StatusInternalServerError, &responseCreateGameHandlerError{
			Code: http.StatusInternalServerError,
			Text: "cannot create game",
		})
		return
	}

	id, err := h.groupManager.Add(group)
	if err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))

		switch err {
		case connections.ErrGroupLimitReached:
			h.writeResponseJSON(w, http.StatusServiceUnavailable, &responseCreateGameHandlerError{
				Code: http.StatusServiceUnavailable,
				Text: "groups limit reached",
			})
		case connections.ErrConnsLimitReached:
			h.writeResponseJSON(w, http.StatusServiceUnavailable, &responseCreateGameHandlerError{
				Code: http.StatusServiceUnavailable,
				Text: "connections limit reached",
			})
		default:
			h.writeResponseJSON(w, http.StatusInternalServerError, &responseCreateGameHandlerError{
				Code: http.StatusInternalServerError,
				Text: "unknown error",
			})
		}
		return
	}

	h.logger.Info("start group")
	group.Start()

	h.logger.WithField("group_id", id).Infoln("created group")

	h.writeResponseJSON(w, http.StatusCreated, &responseCreateGameHandler{
		ID:     id,
		Limit:  group.GetLimit(),
		Width:  uint8(mapWidth),
		Height: uint8(mapHeight),
	})
}

func (h *createGameHandler) writeResponseJSON(w http.ResponseWriter, statusCode int, response interface{}) {
	w.WriteHeader(statusCode)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("X-Content-Type-Options", "nosniff")

	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Error(ErrCreateGameHandler(err.Error()))
	}
}
<|endoftext|>


<commit_before>package terraform

// The main version number that is being run at the moment.
const Version = "0.6.13"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"
<commit_msg>v0.6.13<commit_after>package terraform

// The main version number that is being run at the moment.
const Version = "0.6.13"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<|endoftext|>


<commit_before>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    "bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w io.Writer, m image.Image) {
    buffer := bufio.NewWriter(w)

    buffer.WriteByte('G')
    buffer.WriteByte('I')
    buffer.WriteByte('F')
    buffer.WriteByte('8')
    buffer.WriteByte('9')
    buffer.WriteByte('a')

    b := m.Bounds()
    buffer.WriteByte(byte(b.Max.X / 255)) // Image width, LSB.
    buffer.WriteByte(byte(b.Max.X % 255)) // Image width, MSB.
    buffer.WriteByte(byte(b.Max.Y / 255)) // Image height, LSB.
    buffer.WriteByte(byte(b.Max.Y % 255)) // Image height, MSB.

    buffer.WriteByte(byte(0xF7)) // GCT follows for 256 colors with resolution
                                 // 3 x 8 bits/primary
    buffer.WriteByte(byte(0x00)) // Background color.
    buffer.WriteByte(byte(0x00)) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := 0; i < 256; i++ {
        buffer.WriteByte(byte(i)) // Repeat the same color value to get
        buffer.WriteByte(byte(i)) // grayscale.
        buffer.WriteByte(byte(i))
    }

    buffer.WriteByte(byte(0x21)) // Application Extension block.
    buffer.WriteByte(byte(0xF9)) // Application Extension block (cont).
    buffer.WriteByte(byte(0x0B)) // Next 11 bytes are Application Extension.
    buffer.WriteByte('N') // 8 Character application name.
    buffer.WriteByte('E')
    buffer.WriteByte('T')
    buffer.WriteByte('S')
    buffer.WriteByte('C')
    buffer.WriteByte('A')
    buffer.WriteByte('P')
    buffer.WriteByte('E')
    buffer.WriteByte('2')
    buffer.WriteByte('.')
    buffer.WriteByte('0')
    buffer.WriteByte(byte(0x03)) // 3 more bytes of Application Extension.
    buffer.WriteByte(byte(0x01)) // Data sub-block index (always 1).
    buffer.WriteByte(byte(0xFF)) // Number of repetitions, LSB.
    buffer.WriteByte(byte(0xFF)) // Number of repetitions, MSB.
    buffer.WriteByte(byte(0x00)) // End of Application Extension block.

    buffer.Flush()
}

func writeFrameHeader(w io.Writer, m image.Image) {
    buffer := bufio.NewWriter(w)

    buffer.WriteByte(byte(0x21)) // Start of Graphic Control Extension.
    buffer.WriteByte(byte(0xF9)) // Start of Graphic Control Extension (cont).
    buffer.WriteByte(byte(0x04)) // 4 more bytes of GCE.

    buffer.WriteByte(byte(0x00)) // There is no transparent pixel.
    buffer.WriteByte(byte(0x10)) // Animation delay, in centiseconds, LSB.
    buffer.WriteByte(byte(0x00)) // Animation delay, in centiseconds, MSB.
    buffer.WriteByte(byte(0x00)) // Transparent color #, if we were using.
    buffer.WriteByte(byte(0x00)) // End of Application Extension data.

    buffer.WriteByte(byte(0x2C)) // Start of Image Descriptor.

    b := m.Bounds()
    buffer.WriteByte(byte(b.Min.X / 255))
    buffer.WriteByte(byte(b.Min.X % 255))
    buffer.WriteByte(byte(b.Min.Y / 255))
    buffer.WriteByte(byte(b.Min.Y % 255))

    buffer.WriteByte(byte(b.Max.X / 255))
    buffer.WriteByte(byte(b.Max.X % 255))
    buffer.WriteByte(byte(b.Max.Y / 255))
    buffer.WriteByte(byte(b.Max.Y % 255))

    buffer.WriteByte(byte(0x00)) // No local color table.

    buffer.WriteByte(byte(0x08)) // Start of LZW with minimum code size 8.

    buffer.Flush()
}

func compressImage(m image.Image) *bytes.Buffer {
    b := m.Bounds()

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
        }
    }
    lzww.Close()

    return compressedImageBuffer
}

func writeBlocks(w io.Writer, m image.Image) {
    compressedImage := compressImage(m)

    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImage.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.Write([]byte{byte(blockSize)})
        }

        b, _ :=  compressedImage.ReadByte()
        writeFrameHeader(w, m)
        w.Write([]byte{b})

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }
}

func Encode(w io.Writer, m image.Image) error {
    return EncodeAll(w, []image.Image{m})
}

func EncodeAll(w io.Writer, images []image.Image) error {
    writeHeader(w, images[0])
    for _, m := range images {
        writeBlocks(w, m)
    }
    w.Write([]byte{0, ';'})

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    for x := 0; x < 100; x++ {
        for y := 0; y < 100; y++ {
            c := byte(x ^ y)
            m.Set(x, y, color.RGBA{c, c, c, 0xFF})
        }
    }
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<commit_msg>Use a single buffer<commit_after>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    "bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w *bufio.Writer, m image.Image) {
    w.WriteByte('G')
    w.WriteByte('I')
    w.WriteByte('F')
    w.WriteByte('8')
    w.WriteByte('9')
    w.WriteByte('a')

    b := m.Bounds()
    w.WriteByte(byte(b.Max.X / 255)) // Image width, LSB.
    w.WriteByte(byte(b.Max.X % 255)) // Image width, MSB.
    w.WriteByte(byte(b.Max.Y / 255)) // Image height, LSB.
    w.WriteByte(byte(b.Max.Y % 255)) // Image height, MSB.

    w.WriteByte(byte(0xF7)) // GCT follows for 256 colors with resolution
                                 // 3 x 8 bits/primary
    w.WriteByte(byte(0x00)) // Background color.
    w.WriteByte(byte(0x00)) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := 0; i < 256; i++ {
        w.WriteByte(byte(i)) // Repeat the same color value to get
        w.WriteByte(byte(i)) // grayscale.
        w.WriteByte(byte(i))
    }

    w.WriteByte(byte(0x21)) // Application Extension block.
    w.WriteByte(byte(0xF9)) // Application Extension block (cont).
    w.WriteByte(byte(0x0B)) // Next 11 bytes are Application Extension.
    w.WriteByte('N') // 8 Character application name.
    w.WriteByte('E')
    w.WriteByte('T')
    w.WriteByte('S')
    w.WriteByte('C')
    w.WriteByte('A')
    w.WriteByte('P')
    w.WriteByte('E')
    w.WriteByte('2')
    w.WriteByte('.')
    w.WriteByte('0')
    w.WriteByte(byte(0x03)) // 3 more bytes of Application Extension.
    w.WriteByte(byte(0x01)) // Data sub-block index (always 1).
    w.WriteByte(byte(0xFF)) // Number of repetitions, LSB.
    w.WriteByte(byte(0xFF)) // Number of repetitions, MSB.
    w.WriteByte(byte(0x00)) // End of Application Extension block.
}

func writeFrameHeader(w *bufio.Writer, m image.Image) {
    w.WriteByte(byte(0x21)) // Start of Graphic Control Extension.
    w.WriteByte(byte(0xF9)) // Start of Graphic Control Extension (cont).
    w.WriteByte(byte(0x04)) // 4 more bytes of GCE.

    w.WriteByte(byte(0x00)) // There is no transparent pixel.
    w.WriteByte(byte(0x10)) // Animation delay, in centiseconds, LSB.
    w.WriteByte(byte(0x00)) // Animation delay, in centiseconds, MSB.
    w.WriteByte(byte(0x00)) // Transparent color #, if we were using.
    w.WriteByte(byte(0x00)) // End of Application Extension data.

    w.WriteByte(byte(0x2C)) // Start of Image Descriptor.

    b := m.Bounds()
    w.WriteByte(byte(b.Min.X / 255))
    w.WriteByte(byte(b.Min.X % 255))
    w.WriteByte(byte(b.Min.Y / 255))
    w.WriteByte(byte(b.Min.Y % 255))

    w.WriteByte(byte(b.Max.X / 255))
    w.WriteByte(byte(b.Max.X % 255))
    w.WriteByte(byte(b.Max.Y / 255))
    w.WriteByte(byte(b.Max.Y % 255))

    w.WriteByte(byte(0x00)) // No local color table.

    w.WriteByte(byte(0x08)) // Start of LZW with minimum code size 8.
}

func compressImage(m image.Image) *bytes.Buffer {
    b := m.Bounds()

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
        }
    }
    lzww.Close()

    return compressedImageBuffer
}

func writeFrame(w *bufio.Writer, m image.Image) {
    writeFrameHeader(w, m)

    compressedImage := compressImage(m)

    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImage.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.WriteByte(byte(blockSize))
        }

        b, _ :=  compressedImage.ReadByte()
        w.WriteByte(b)

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }
}

func Encode(w io.Writer, m image.Image) error {
    return EncodeAll(w, []image.Image{m})
}

func EncodeAll(w io.Writer, images []image.Image) error {
    buffer := bufio.NewWriter(w)

    writeHeader(buffer, images[0])
    for _, m := range images {
        writeFrame(buffer, m)
    }
    buffer.WriteByte(';')
    buffer.Flush()

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    for x := 0; x < 100; x++ {
        for y := 0; y < 100; y++ {
            c := byte(x ^ y)
            m.Set(x, y, color.RGBA{c, c, c, 0xFF})
        }
    }
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<|endoftext|>


<commit_before>package command

import (
	"log"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/sclevine/spec"
	"github.com/sclevine/spec/report"
)

func TestRunner(t *testing.T) {
	spec.Run(t, "Regexp", testRegexp, spec.Report(report.Terminal{}))
}

type Case struct {
	input   string
	matches bool
	args    []string
}

func testRegexp(t *testing.T, when spec.G, it spec.S) {
	var cases []Case

	it.Before(func() {
		RegisterTestingT(t)
		log.SetFlags(log.Llongfile)
		cases = []Case{
			{
				input:   "//go:generate counterfeiter . Intf",
				matches: false,
			},
			{
				input:   "//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Intf",
				matches: false,
			},
			{
				input:   "//counterfeiter:generate . Intf",
				matches: true,
				args:    []string{".", "Intf"},
			},
			{
				input:   "//go:generate  stringer -type=Enum",
				matches: false,
			},
		}
	})

	it.Focus("splits args correctly", func() {
		Expect(stringToArgs(". Intf")).To(ConsistOf([]string{"counterfeiter", ".", "Intf"}))
		Expect(stringToArgs("    .    Intf     ")).To(ConsistOf([]string{"counterfeiter", ".", "Intf"}))
	})

	it("matches lines appropriately", func() {
		for _, c := range cases {
			result, ok := matchForString(c.input)
			if c.matches {
				Expect(ok).To(BeTrue())
				Expect(result).To(ConsistOf(c.args))
			} else {
				Expect(ok).To(BeFalse())
			}
		}
	})
}
<commit_msg>unfocus test<commit_after>package command

import (
	"log"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/sclevine/spec"
	"github.com/sclevine/spec/report"
)

func TestRunner(t *testing.T) {
	spec.Run(t, "Regexp", testRegexp, spec.Report(report.Terminal{}))
}

type Case struct {
	input   string
	matches bool
	args    []string
}

func testRegexp(t *testing.T, when spec.G, it spec.S) {
	var cases []Case

	it.Before(func() {
		RegisterTestingT(t)
		log.SetFlags(log.Llongfile)
		cases = []Case{
			{
				input:   "//go:generate counterfeiter . Intf",
				matches: false,
			},
			{
				input:   "//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Intf",
				matches: false,
			},
			{
				input:   "//counterfeiter:generate . Intf",
				matches: true,
				args:    []string{".", "Intf"},
			},
			{
				input:   "//go:generate  stringer -type=Enum",
				matches: false,
			},
		}
	})

	it("splits args correctly", func() {
		Expect(stringToArgs(". Intf")).To(ConsistOf([]string{"counterfeiter", ".", "Intf"}))
		Expect(stringToArgs("    .    Intf     ")).To(ConsistOf([]string{"counterfeiter", ".", "Intf"}))
	})

	it("matches lines appropriately", func() {
		for _, c := range cases {
			result, ok := matchForString(c.input)
			if c.matches {
				Expect(ok).To(BeTrue())
				Expect(result).To(ConsistOf(c.args))
			} else {
				Expect(ok).To(BeFalse())
			}
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	flag "github.com/ogier/pflag"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"
)

const (
	version string = "0.1"
)

var (
	downloaded []int64
	boards     string = "a|b|c|d|e|f|g|gif|h|hr|k|m|o|p|r|s|t|u|v|vg|w|wg|i|ic|r9k|cm|hm|y|3|adv|an|cgl|ck|co|diy|fa|fit|hc|int|jp|lit|mlp|mu|n|po|pol|sci|soc|sp|tg|toy|trv|tv|vp|wsg|x|q"

	// Options
	minWidth     *int64         = flag.Int64P("min-width", "w", 0, "Minimum width of images")
	minHeight    *int64         = flag.Int64P("min-height", "h", 0, "Minimum height of images")
	refresh      *time.Duration = flag.DurationP("refresh", "r", time.Second*30, "Refresh rate (min 30s)")
	orignalNames *bool          = flag.BoolP("original-names", "o", false, "Save images under original filenames")
	showVersion  *bool          = flag.BoolP("version", "v", false, "Show version")
)

type Post struct {
	No             int64
	Resto          int64
	Sticky         bool
	Closed         bool
	Now            string
	Time           int64
	Name           string
	Trip           string
	Id             string
	Capcode        string
	Country        string
	Country_name   string
	Email          string
	Sub            string
	Com            string
	Tim            int64
	Filename       string
	Ext            string
	Fsize          int64
	Md5            string
	W              int64
	H              int64
	Tn_w           int64
	Tn_h           int64
	Filedeleted    bool
	Spoiler        bool
	Custom_spoiler bool
	Omitted_posts  int64
	Omitted_images int64
}

func (p *Post) FullFileName(original bool) (s string) {
	if original {
		s = fmt.Sprintf("%s%s", p.Filename, p.Ext)
	} else {
		s = fmt.Sprintf("%d%s", p.Tim, p.Ext)
	}
	return
}

type Thread struct {
	Posts []Post
}

func checkError(err error) {
	if err != nil {
		log.Println(err.Error())
		os.Exit(1)
	}
}

func parseThreadId(s string) (b, id string, err error) {
	r, err := regexp.Compile(fmt.Sprintf("^/?(%s)/(?:res/)?(\\d+)", boards))
	if err != nil {
		return b, id, err
	}

	groups := r.FindSubmatch([]byte(s))
	if len(groups) < 1 {
		return b, id, errors.New("Input does not match regex")
	}

	b = string(groups[1])
	id = string(groups[2])

	return b, id, nil
}

func parseThreadFromJson(r io.Reader) (Thread, error) {
	dec := json.NewDecoder(r)
	var t Thread
	for {
		if err := dec.Decode(&t); err == io.EOF {
			break
		} else if err != nil {
			return t, err
		}
	}
	return t, nil
}

func downloadImage(board string, p Post) {
	if p.W < *minWidth || p.H < *minHeight || p.Filedeleted {
		downloaded = append(downloaded, p.Tim)
		return
	}

	url := fmt.Sprintf("https://images.4chan.org/%s/src/%d%s", board, p.Tim, p.Ext)
	img, _, err := getUrl(url)
	checkError(err)

	err = ioutil.WriteFile(p.FullFileName(*orignalNames), img, 0644)
	checkError(err)

	downloaded = append(downloaded, p.Tim)
}

func getUrl(url string) ([]byte, int, error) {
	resp, err := http.Get(url)
	if err != nil {
		return []byte{}, 0, nil
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, 0, nil
	}
	return body, resp.StatusCode, nil

}

func wasDownloaded(p Post) bool {
	for _, e := range downloaded {
		if e == p.Tim {
			return true
		}
	}
	return false
}

func loadThread(board string, id string) {
	url := fmt.Sprintf("https://api.4chan.org/%s/res/%s.json", board, id)
	resp, status, err := getUrl(url)
	checkError(err)
	if status != 200 {
		if status == 404 {
			log.Println("Thread 404'd")
			os.Exit(1)
		}
		fmt.Printf("Something went wrong\nGot return code %d at '%s'.\n", status, url)
		return
	}

	thread, err := parseThreadFromJson(bytes.NewReader(resp))
	if err != nil {
		log.Println("Could not parse thread")
	}

	for _, e := range thread.Posts {
		if !wasDownloaded(e) {
			go downloadImage(board, e)
		}
	}
}

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: chanloader [options] /b/res/123456\nOptions:")
		flag.PrintDefaults()
	}
	flag.Parse()
	if *refresh < time.Second*30 {
		*refresh = time.Second * 30
	}

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		flag.Usage()
		os.Exit(1)
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	board, id, err := parseThreadId(flag.Arg(0))
	if err != nil {
		fmt.Printf("Invalid input: %s\n", flag.Arg(0))
		flag.Usage()
		os.Exit(1)
	}

	ticker := time.NewTicker(*refresh)
	for {
		log.Println("Loading thread")
		go loadThread(board, id)
		<-ticker.C
	}
}
<commit_msg>Set version to 0.0.1<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	flag "github.com/ogier/pflag"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"
)

const (
	version string = "0.0.1"
)

var (
	downloaded []int64
	boards     string = "a|b|c|d|e|f|g|gif|h|hr|k|m|o|p|r|s|t|u|v|vg|w|wg|i|ic|r9k|cm|hm|y|3|adv|an|cgl|ck|co|diy|fa|fit|hc|int|jp|lit|mlp|mu|n|po|pol|sci|soc|sp|tg|toy|trv|tv|vp|wsg|x|q"

	// Options
	minWidth     *int64         = flag.Int64P("min-width", "w", 0, "Minimum width of images")
	minHeight    *int64         = flag.Int64P("min-height", "h", 0, "Minimum height of images")
	refresh      *time.Duration = flag.DurationP("refresh", "r", time.Second*30, "Refresh rate (min 30s)")
	orignalNames *bool          = flag.BoolP("original-names", "o", false, "Save images under original filenames")
	showVersion  *bool          = flag.BoolP("version", "v", false, "Show version")
)

type Post struct {
	No             int64
	Resto          int64
	Sticky         bool
	Closed         bool
	Now            string
	Time           int64
	Name           string
	Trip           string
	Id             string
	Capcode        string
	Country        string
	Country_name   string
	Email          string
	Sub            string
	Com            string
	Tim            int64
	Filename       string
	Ext            string
	Fsize          int64
	Md5            string
	W              int64
	H              int64
	Tn_w           int64
	Tn_h           int64
	Filedeleted    bool
	Spoiler        bool
	Custom_spoiler bool
	Omitted_posts  int64
	Omitted_images int64
}

func (p *Post) FullFileName(original bool) (s string) {
	if original {
		s = fmt.Sprintf("%s%s", p.Filename, p.Ext)
	} else {
		s = fmt.Sprintf("%d%s", p.Tim, p.Ext)
	}
	return
}

type Thread struct {
	Posts []Post
}

func checkError(err error) {
	if err != nil {
		log.Println(err.Error())
		os.Exit(1)
	}
}

func parseThreadId(s string) (b, id string, err error) {
	r, err := regexp.Compile(fmt.Sprintf("^/?(%s)/(?:res/)?(\\d+)", boards))
	if err != nil {
		return b, id, err
	}

	groups := r.FindSubmatch([]byte(s))
	if len(groups) < 1 {
		return b, id, errors.New("Input does not match regex")
	}

	b = string(groups[1])
	id = string(groups[2])

	return b, id, nil
}

func parseThreadFromJson(r io.Reader) (Thread, error) {
	dec := json.NewDecoder(r)
	var t Thread
	for {
		if err := dec.Decode(&t); err == io.EOF {
			break
		} else if err != nil {
			return t, err
		}
	}
	return t, nil
}

func downloadImage(board string, p Post) {
	if p.W < *minWidth || p.H < *minHeight || p.Filedeleted {
		downloaded = append(downloaded, p.Tim)
		return
	}

	url := fmt.Sprintf("https://images.4chan.org/%s/src/%d%s", board, p.Tim, p.Ext)
	img, _, err := getUrl(url)
	checkError(err)

	err = ioutil.WriteFile(p.FullFileName(*orignalNames), img, 0644)
	checkError(err)

	downloaded = append(downloaded, p.Tim)
}

func getUrl(url string) ([]byte, int, error) {
	resp, err := http.Get(url)
	if err != nil {
		return []byte{}, 0, nil
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, 0, nil
	}
	return body, resp.StatusCode, nil

}

func wasDownloaded(p Post) bool {
	for _, e := range downloaded {
		if e == p.Tim {
			return true
		}
	}
	return false
}

func loadThread(board string, id string) {
	url := fmt.Sprintf("https://api.4chan.org/%s/res/%s.json", board, id)
	resp, status, err := getUrl(url)
	checkError(err)
	if status != 200 {
		if status == 404 {
			log.Println("Thread 404'd")
			os.Exit(1)
		}
		fmt.Printf("Something went wrong\nGot return code %d at '%s'.\n", status, url)
		return
	}

	thread, err := parseThreadFromJson(bytes.NewReader(resp))
	if err != nil {
		log.Println("Could not parse thread")
	}

	for _, e := range thread.Posts {
		if !wasDownloaded(e) {
			go downloadImage(board, e)
		}
	}
}

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: chanloader [options] /b/res/123456\nOptions:")
		flag.PrintDefaults()
	}
	flag.Parse()
	if *refresh < time.Second*30 {
		*refresh = time.Second * 30
	}

	if *showVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	if flag.NArg() < 1 {
		flag.Usage()
		os.Exit(1)
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	board, id, err := parseThreadId(flag.Arg(0))
	if err != nil {
		fmt.Printf("Invalid input: %s\n", flag.Arg(0))
		flag.Usage()
		os.Exit(1)
	}

	ticker := time.NewTicker(*refresh)
	for {
		log.Println("Loading thread")
		go loadThread(board, id)
		<-ticker.C
	}
}
<|endoftext|>


<commit_before>package oauth

import (
	"io/ioutil"
	"log"
	"os"
	"testing"

	"github.com/RichardKnop/go-oauth2-server/config"
	"github.com/RichardKnop/go-oauth2-server/migrations"
	"github.com/areatech/go-fixtures"
	"github.com/jinzhu/gorm"
	"github.com/stretchr/testify/suite"
	// sqlite driver
	_ "github.com/mattn/go-sqlite3"
)

var testDbPath = "/tmp/oauth_testdb.sqlite"

// OauthTestSuite needs to be exported so the tests run
type OauthTestSuite struct {
	suite.Suite
	cnf     *config.Config
	db      *gorm.DB
	service *Service
	client  *Client
	user    *User
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *OauthTestSuite) SetupSuite() {
	// Delete the test database
	os.Remove(testDbPath)

	// Initialise the config
	suite.cnf = config.NewConfig()

	// Init in-memory test database
	inMemoryDB, err := gorm.Open("sqlite3", testDbPath)
	if err != nil {
		log.Fatal(err)
	}
	suite.db = &inMemoryDB

	// Run all migrations
	if err := migrations.Bootstrap(suite.db); err != nil {
		log.Print(err)
	}
	if err := MigrateAll(suite.db); err != nil {
		log.Print(err)
	}

	// Load test data from fixtures
	for _, path := range []string{
		"../fixtures/test_data.yml",
	} {
		// Read fixture data from the file
		data, err := ioutil.ReadFile(path)
		if err != nil {
			log.Fatal(err)
		}

		// Insert the fixture data
		err = fixtures.Load(data, suite.db.DB(), suite.cnf.Database.Type)
		if err != nil {
			log.Fatal(err)
		}
	}

	// Fetch the test client
	suite.client = new(Client)
	if err := suite.db.First(suite.client, 1).Error; err != nil {
		log.Fatal(err)
	}

	// Fetch the test user
	suite.user = new(User)
	if err := suite.db.First(suite.user, 1).Error; err != nil {
		log.Fatal(err)
	}

	// Initialise the service
	suite.service = NewService(suite.cnf, suite.db)
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *OauthTestSuite) TearDownSuite() {
	//
}

// The SetupTest method will be run before every test in the suite.
func (suite *OauthTestSuite) SetupTest() {
	//
}

// The TearDownTest method will be run after every test in the suite.
func (suite *OauthTestSuite) TearDownTest() {
	// Scopes are static, populated from fixtures,
	// so there is no need to clear them after running a test
	suite.db.Unscoped().Delete(AuthorizationCode{})
	suite.db.Unscoped().Delete(RefreshToken{})
	suite.db.Unscoped().Delete(AccessToken{})
	suite.db.Unscoped().Not("id", suite.user.ID).Delete(User{})
	suite.db.Unscoped().Not("id", suite.client.ID).Delete(Client{})
}

// TestOauthTestSuite ...
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestOauthTestSuite(t *testing.T) {
	suite.Run(t, new(OauthTestSuite))
}
<commit_msg>Update suite_test.go<commit_after>package oauth

import (
	"io/ioutil"
	"log"
	"os"
	"testing"

	"github.com/RichardKnop/go-oauth2-server/config"
	"github.com/RichardKnop/go-oauth2-server/migrations"
	"github.com/areatech/go-fixtures"
	"github.com/jinzhu/gorm"
	"github.com/stretchr/testify/suite"
	// sqlite driver
	_ "github.com/mattn/go-sqlite3"
)

var testDbPath = "/tmp/oauth_testdb.sqlite"

// OauthTestSuite needs to be exported so the tests run
type OauthTestSuite struct {
	suite.Suite
	cnf     *config.Config
	db      *gorm.DB
	service *Service
	client  *Client
	user    *User
}

// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *OauthTestSuite) SetupSuite() {
	// Delete the test database
	os.Remove(testDbPath)

	// Initialise the config
	suite.cnf = config.NewConfig()

	// Init in-memory test database
	inMemoryDB, err := gorm.Open("sqlite3", testDbPath)
	if err != nil {
		log.Fatal(err)
	}
	suite.db = &inMemoryDB

	// Run all migrations
	if err := migrations.Bootstrap(suite.db); err != nil {
		log.Print(err)
	}
	if err := MigrateAll(suite.db); err != nil {
		log.Print(err)
	}

	// Load test data from fixtures
	for _, path := range []string{
		"../fixtures/test_data.yml",
	} {
		// Read fixture data from the file
		data, err := ioutil.ReadFile(path)
		if err != nil {
			log.Fatal(err)
		}

		// Insert the fixture data
		err = fixtures.Load(data, suite.db.DB(), suite.cnf.Database.Type)
		if err != nil {
			log.Fatal(err)
		}
	}

	// Fetch the test client
	suite.client = new(Client)
	if err := suite.db.First(suite.client, 1).Error; err != nil {
		log.Fatal(err)
	}

	// Fetch the test user
	suite.user = new(User)
	if err := suite.db.First(suite.user, 1).Error; err != nil {
		log.Fatal(err)
	}

	// Initialise the service
	suite.service = NewService(suite.cnf, suite.db)
}

// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *OauthTestSuite) TearDownSuite() {
	//
}

// The SetupTest method will be run before every test in the suite.
func (suite *OauthTestSuite) SetupTest() {
	//
}

// The TearDownTest method will be run after every test in the suite.
func (suite *OauthTestSuite) TearDownTest() {
	// Scopes are static, populated from fixtures,
	// so there is no need to clear them after running a test
	suite.db.Unscoped().Delete(new(AuthorizationCode))
	suite.db.Unscoped().Delete(new(RefreshToken))
	suite.db.Unscoped().Delete(new(AccessToken))
	suite.db.Unscoped().Not("id", suite.user.ID).Delete(new(User))
	suite.db.Unscoped().Not("id", suite.client.ID).Delete(new(Client))
}

// TestOauthTestSuite ...
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestOauthTestSuite(t *testing.T) {
	suite.Run(t, new(OauthTestSuite))
}
<|endoftext|>