<commit_before>package petrel

// Copyright (c) 2014-2016 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for petrel

import (
	"bytes"
	"encoding/binary"
	"io"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (h *Server) sockAccept() {
	defer h.w.Done()
	var cn uint
	for cn = 1; true; cn++ {
		c, err := h.l.Accept()
		if err != nil {
			select {
			case <-h.q:
				// h.Quit() was invoked; close up shop
				h.genMsg(0, 0, perrs["quit"], "", nil)
				return
			default:
				// we've had a networking error
				h.genMsg(0, 0, perrs["listenerfail"], "", err)
				return
			}
		}
		// we have a new client
		h.w.Add(1)
		go h.connServer(c, cn)
	}
}

// connServer dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (h *Server) connServer(c net.Conn, cn uint) {
	defer h.w.Done()
	defer c.Close()
	// request counter for this connection
	var reqnum uint

	if h.li {
		h.genMsg(cn, reqnum, perrs["connect"], c.RemoteAddr().String(), nil)
	} else {
		h.genMsg(cn, reqnum, perrs["connect"], "", nil)
	}

	for {
		reqnum++
		// read the request
		req, perr, xtra, err := h.connRead(c, cn, reqnum)
		if perr != "" {
			h.genMsg(cn, reqnum, perrs[perr], xtra, err)
			if perrs[perr].xmit != nil {
				err = h.send(c, cn, reqnum, perrs[perr].xmit)
				if err != nil {
					return
				}
			}
			//TODO send "you've been disconnected" msg
			return
		}
		if len(req) == 0 {
			h.genMsg(cn, reqnum, perrs["nilreq"], "", nil)
			err = h.send(c, cn, reqnum, perrs["nilreq"].xmit)
			if err != nil {
				return
			}
			continue
		}

		// dispatch the request and get the reply
		reply, perr, xtra, err := h.reqDispatch(c, cn, reqnum, req)
		if perr != "" {
			h.genMsg(cn, reqnum, perrs[perr], xtra, err)
			if perrs[perr].xmit != nil {
				err = h.send(c, cn, reqnum, perrs[perr].xmit)
				if err != nil {
					return
				}
			}
			continue
		}

		// send reply
		err = h.send(c, cn, reqnum, reply)
		if err != nil {
			return
		}
		h.genMsg(cn, reqnum, perrs["success"], "", nil)
	}
}

// connRead does all network reads and assembles the request. If it
// returns an error, then the connection terminates because the state
// of the connection cannot be known.
func (h *Server) connRead(c net.Conn, cn, reqnum uint) ([]byte, string, string, error) {
	// buffer 0 holds the message length
	b0 := make([]byte, 4)
	// buffer 1: network reads go here, 128B at a time
	b1 := make([]byte, 128)
	// buffer 2: data accumulates here; requests pulled from here
	var b2 []byte
	// message length
	var mlen int32
	// bytes read so far
	var bread int32

	// get the response message length
	if h.t > 0 {
		c.SetReadDeadline(time.Now().Add(h.t))
	}
	n, err := c.Read(b0)
	if err != nil {
		if err == io.EOF {
			return nil, "disconnect", "", err
		}
		return nil, "netreaderr", "no message length", err
	}
	if  n != 4 {
		return nil, "netreaderr", "short read on message length", err
	}
	buf := bytes.NewReader(b0)
	err = binary.Read(buf, binary.BigEndian, &mlen)
	if err != nil {
		return nil, "internalerr", "could not decode message length", err
	}

	for bread < mlen {
		// if there are less than 128 bytes remaining to read in this
		// message, resize b1 to fit. this avoids reading across a
		// message boundary.
		if x := mlen - bread; x < 128 {
			b1 = make([]byte, x)
		}
		if h.t > 0 {
			c.SetReadDeadline(time.Now().Add(h.t))
		}
		n, err = c.Read(b1)
		if err != nil {
			if err == io.EOF {
				return nil, "disconnect", "", err
			}
			return nil, "netreaderr", "failed to read req from socket", err
		}
		if n == 0 {
			// short-circuit just in case this ever manages to happen
			return b2[:mlen], "", "", err
		}
		bread += int32(n)
		if h.rl > 0 && bread > h.rl {
			return nil, "reqlen", "", nil
		}
		b2 = append(b2, b1[:n]...)
	}
	return b2[:mlen], "", "", err
}

// reqDispatch turns the request into a command and arguments, and
// dispatches these components to a handler.
func (h *Server) reqDispatch(c net.Conn, cn, reqnum uint, req []byte) ([]byte, string, string, error) {
	// get chunk locations
	cl := qsplit.LocationsOnce(req)
	dcmd := string(req[cl[0]:cl[1]])
	// now get the args
	var dargs []byte
	if cl[2] != -1 {
		dargs = req[cl[2]:]
	}
	// send error if we don't recognize the command
	responder, ok := h.d[dcmd]
	if !ok {
		return nil, "badreq", dcmd, nil
	}
	// ok, we know the command and we have its dispatch
	// func. call it and send response
	h.genMsg(cn, reqnum, perrs["dispatch"], dcmd, nil)
	var rs [][]byte // req, split by word
	switch responder.mode {
	case "args":
		rs = qsplit.ToBytes(dargs)
	case "blob":
		rs = rs[:0]
		rs = append(rs, dargs)
	}
	response, err := responder.r(rs)
	if err != nil {
		return nil, "reqerr", "", err
	}
	return response, "", "", nil
}

// send handles all network writes.
func (h *Server) send(c net.Conn, cn, reqnum uint, resp []byte) error {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, int32(len(resp)))
	if err != nil {
		h.genMsg(cn, reqnum, perrs["internalerr"], "could not encode message length", err)
		return err
	}
	resp = append(buf.Bytes(), resp...)
	if h.t > 0 {
		c.SetReadDeadline(time.Now().Add(h.t))
	}
	_, err = c.Write(resp)
	if err != nil {
		h.genMsg(cn, reqnum, perrs["netwriteerr"], "", err)
		return err
	}
	return err
}
<commit_msg>network generalization: connection id and reqnum were needlessly being passed to Server.connRead<commit_after>package petrel

// Copyright (c) 2014-2016 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for petrel

import (
	"bytes"
	//"crypto/hmac"
	"encoding/binary"
	"io"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (h *Server) sockAccept() {
	defer h.w.Done()
	var cn uint
	for cn = 1; true; cn++ {
		c, err := h.l.Accept()
		if err != nil {
			select {
			case <-h.q:
				// h.Quit() was invoked; close up shop
				h.genMsg(0, 0, perrs["quit"], "", nil)
				return
			default:
				// we've had a networking error
				h.genMsg(0, 0, perrs["listenerfail"], "", err)
				return
			}
		}
		// we have a new client
		h.w.Add(1)
		go h.connServer(c, cn)
	}
}

// connServer dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (h *Server) connServer(c net.Conn, cn uint) {
	defer h.w.Done()
	defer c.Close()
	// request counter for this connection
	var reqnum uint

	if h.li {
		h.genMsg(cn, reqnum, perrs["connect"], c.RemoteAddr().String(), nil)
	} else {
		h.genMsg(cn, reqnum, perrs["connect"], "", nil)
	}

	for {
		reqnum++
		// read the request
		req, perr, xtra, err := h.connRead(c)
		if perr != "" {
			h.genMsg(cn, reqnum, perrs[perr], xtra, err)
			if perrs[perr].xmit != nil {
				err = h.send(c, cn, reqnum, perrs[perr].xmit)
				if err != nil {
					return
				}
			}
			//TODO send "you've been disconnected" msg
			return
		}
		if len(req) == 0 {
			h.genMsg(cn, reqnum, perrs["nilreq"], "", nil)
			err = h.send(c, cn, reqnum, perrs["nilreq"].xmit)
			if err != nil {
				return
			}
			continue
		}

		// dispatch the request and get the reply
		reply, perr, xtra, err := h.reqDispatch(c, cn, reqnum, req)
		if perr != "" {
			h.genMsg(cn, reqnum, perrs[perr], xtra, err)
			if perrs[perr].xmit != nil {
				err = h.send(c, cn, reqnum, perrs[perr].xmit)
				if err != nil {
					return
				}
			}
			continue
		}

		// send reply
		err = h.send(c, cn, reqnum, reply)
		if err != nil {
			return
		}
		h.genMsg(cn, reqnum, perrs["success"], "", nil)
	}
}

// connRead does all network reads and assembles the request. If it
// returns an error, then the connection terminates because the state
// of the connection cannot be known.
func (h *Server) connRead(c net.Conn) ([]byte, string, string, error) {
	// buffer 0 holds the message length
	b0 := make([]byte, 4)
	// buffer 1: network reads go here, 128B at a time
	b1 := make([]byte, 128)
	// buffer 2: data accumulates here; requests pulled from here
	var b2 []byte
	// message length
	var mlen int32
	// bytes read so far
	var bread int32

	// get the response message length
	if h.t > 0 {
		c.SetReadDeadline(time.Now().Add(h.t))
	}
	n, err := c.Read(b0)
	if err != nil {
		if err == io.EOF {
			return nil, "disconnect", "", err
		}
		return nil, "netreaderr", "no message length", err
	}
	if  n != 4 {
		return nil, "netreaderr", "short read on message length", err
	}
	buf := bytes.NewReader(b0)
	err = binary.Read(buf, binary.BigEndian, &mlen)
	if err != nil {
		return nil, "internalerr", "could not decode message length", err
	}

	for bread < mlen {
		// if there are less than 128 bytes remaining to read in this
		// message, resize b1 to fit. this avoids reading across a
		// message boundary.
		if x := mlen - bread; x < 128 {
			b1 = make([]byte, x)
		}
		if h.t > 0 {
			c.SetReadDeadline(time.Now().Add(h.t))
		}
		n, err = c.Read(b1)
		if err != nil {
			if err == io.EOF {
				return nil, "disconnect", "", err
			}
			return nil, "netreaderr", "failed to read req from socket", err
		}
		if n == 0 {
			// short-circuit just in case this ever manages to happen
			return b2[:mlen], "", "", err
		}
		bread += int32(n)
		if h.rl > 0 && bread > h.rl {
			return nil, "reqlen", "", nil
		}
		b2 = append(b2, b1[:n]...)
	}
	return b2[:mlen], "", "", err
}

// reqDispatch turns the request into a command and arguments, and
// dispatches these components to a handler.
func (h *Server) reqDispatch(c net.Conn, cn, reqnum uint, req []byte) ([]byte, string, string, error) {
	// get chunk locations
	cl := qsplit.LocationsOnce(req)
	dcmd := string(req[cl[0]:cl[1]])
	// now get the args
	var dargs []byte
	if cl[2] != -1 {
		dargs = req[cl[2]:]
	}
	// send error if we don't recognize the command
	responder, ok := h.d[dcmd]
	if !ok {
		return nil, "badreq", dcmd, nil
	}
	// ok, we know the command and we have its dispatch
	// func. call it and send response
	h.genMsg(cn, reqnum, perrs["dispatch"], dcmd, nil)
	var rs [][]byte // req, split by word
	switch responder.mode {
	case "args":
		rs = qsplit.ToBytes(dargs)
	case "blob":
		rs = rs[:0]
		rs = append(rs, dargs)
	}
	response, err := responder.r(rs)
	if err != nil {
		return nil, "reqerr", "", err
	}
	return response, "", "", nil
}

// send handles all network writes.
func (h *Server) send(c net.Conn, cn, reqnum uint, resp []byte) error {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, int32(len(resp)))
	if err != nil {
		h.genMsg(cn, reqnum, perrs["internalerr"], "could not encode message length", err)
		return err
	}
	resp = append(buf.Bytes(), resp...)
	if h.t > 0 {
		c.SetReadDeadline(time.Now().Add(h.t))
	}
	_, err = c.Write(resp)
	if err != nil {
		h.genMsg(cn, reqnum, perrs["netwriteerr"], "", err)
		return err
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

var identifierTests = []struct {
	values map[string]map[string]string
	id     Identifier
	found  bool
	value  string
}{
	{
		values: nil,
		id:     Identifier{key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "bar"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "bar"},
		found:  false,
	},
}

func Test_found(t *testing.T) {
	for _, test := range identifierTests {
		got := found(test.values, &test.id)
		if got != test.found {
			t.Errorf("found not correct for %+v (found: %+v, got: %+v)", test.id, test.found, got)
		}
	}
}

func Test_insert(t *testing.T) {
	values := make(map[string]map[string]string)
	id := &Identifier{key: "foo"}
	value := "bar"
	insert(values, id, value)
	v, ok := values[""]["foo"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
	id = &Identifier{scope: "foo", key: "bar"}
	value = "example"
	insert(values, id, value)
	v, ok = values["foo"]["bar"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
}

func Test_lookup(t *testing.T) {
	for _, test := range identifierTests {
		got := lookup(test.values, &test.id)
		if got != test.value {
			t.Errorf("lookup not correct for %+v (found: %+v, got: %+v)", test.id, test.value, got)
		}
	}
}
<commit_msg>add test for collect<commit_after>package main

import (
	"reflect"
	"testing"
)

var identifierTests = []struct {
	values map[string]map[string]string
	id     Identifier
	found  bool
	value  string
}{
	{
		values: nil,
		id:     Identifier{key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{key: "bar"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  true,
		value:  "example",
	},
	{
		values: map[string]map[string]string{"": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "foo"},
		found:  false,
	},
	{
		values: map[string]map[string]string{"example": {"foo": "example"}},
		id:     Identifier{scope: "example", key: "bar"},
		found:  false,
	},
}

func Test_found(t *testing.T) {
	for _, test := range identifierTests {
		got := found(test.values, &test.id)
		if got != test.found {
			t.Errorf("found not correct for %+v (found: %+v, got: %+v)", test.id, test.found, got)
		}
	}
}

func Test_collect(t *testing.T) {
	ids := []*Identifier{
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "bar"},
		&Identifier{scope: "zoo", key: "foo"},
		&Identifier{scope: "foo", key: "foo"},
		&Identifier{scope: "foo", key: "baz"},
		&Identifier{scope: "qux", key: "bar"},
	}
	expectedFoo := &IdentifierGroup{
		scope: "foo",
		keys:  []string{"foo", "bar", "baz"},
	}
	expectedBar := &IdentifierGroup{
		scope: "bar",
		keys:  nil,
	}
	idgFoo := collect(ids, "foo")
	if !reflect.DeepEqual(idgFoo, expectedFoo) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedFoo, idgFoo)
	}
	idgBar := collect(ids, "bar")
	if !reflect.DeepEqual(idgBar, expectedBar) {
		t.Errorf("collect not correct (expected: %+v, got: %+v)", expectedBar, idgBar)
	}
}

func Test_insert(t *testing.T) {
	values := make(map[string]map[string]string)
	id := &Identifier{key: "foo"}
	value := "bar"
	insert(values, id, value)
	v, ok := values[""]["foo"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
	id = &Identifier{scope: "foo", key: "bar"}
	value = "example"
	insert(values, id, value)
	v, ok = values["foo"]["bar"]
	if !ok {
		t.Errorf("insert failed for %+v", id)
	}
	if v != value {
		t.Errorf("insert not correctly for %+v (found: %+v, got: %+v)", id, v, value)
	}
}

func Test_lookup(t *testing.T) {
	for _, test := range identifierTests {
		got := lookup(test.values, &test.id)
		if got != test.value {
			t.Errorf("lookup not correct for %+v (found: %+v, got: %+v)", test.id, test.value, got)
		}
	}
}
<|endoftext|>


<commit_before>package buildrtc

import (
	"fmt"
	y "github.com/shiguredo/yspata"
	"io"
	"os"
)

type Android struct {
	Conf *Config
}

func NewAndroid(conf *Config) *Android {
	return &Android{conf}
}

func (n *Android) ArchiveDir() string {
	return fmt.Sprintf("sora-webrtc-%s-android", n.Conf.WebRTCVersion())
}

func (n *Android) ArchiveZip() string {
	return n.ArchiveDir() + ".zip"
}

func (n *Android) Solutions() string {
	return "solutions = [\n" +
		"  {\n" +
		"    \"url\": \"https://webrtc.googlesource.com/src.git\",\n" +
		"    \"managed\": False,\n" +
		"    \"name\": \"src\",\n" +
		"    \"deps_file\": \"DEPS\",\n" +
		"    \"custom_deps\": {},\n" +
		"  },\n" +
		"]\n" +
		"target_os = [\"android\", \"unix\"]\n"
}

func (n *Android) Build() {
	if n.Conf.Debug {
		n.BuildAAR("debug")
	}
	if n.Conf.Release {
		n.BuildAAR("release")
	}
}

func (n *Android) BuildAAR(conf string) {
	y.Printf("Build Android AAR for %s...", conf)

	wd, _ := os.Getwd()
	os.Chdir(n.Conf.WebRTCSrcDir)
	bldDir := y.Join(n.Conf.BuildDir, fmt.Sprintf("android-%s", conf))
	tempDir := y.Join(bldDir, "build")
	libaar := y.Join(bldDir, n.Conf.AndroidAAR)
	y.Execf("mkdir -p %s", bldDir)

	args := []string{n.Conf.Python, n.Conf.AndroidBuildScript,
		"--output", libaar, "--build-dir", tempDir,
		"--build_config", conf, "--arch"}
	if n.Conf.AndroidArchV7A {
		args = append(args, "armeabi-v7a")
	}
	if n.Conf.AndroidArchV8A {
		args = append(args, "arm64-v8a")
	}
	cmd := y.Command("time", args...)
	cmd.OnStdin = func(w io.WriteCloser) {
		io.WriteString(w, "y\n")
	}
	cmd.Run().FailIf("build failed")

	os.Chdir(wd)
}

func (n *Android) Archive() {
	bldDir := n.Conf.BuildDir
	distDir := n.Conf.DistDir
	distDirDg := y.Join(distDir, "android-debug")
	distDirRl := y.Join(distDir, "android-release")

	// clean
	y.Exec("rm", "-rf", distDir, n.ArchiveDir(), n.ArchiveZip())
	y.Exec("mkdir", distDir)
	y.Exec("mkdir", distDirDg)
	y.Exec("mkdir", distDirRl)

	// library
	y.Exec("cp", y.Join(bldDir, "android-debug", n.Conf.AndroidAAR), distDirDg)
	y.Exec("cp", y.Join(bldDir, "android-release", n.Conf.AndroidAAR), distDirRl)

	// archive
	y.Exec("mv", distDir, n.ArchiveDir())
	y.Exec("zip", "-rq", n.ArchiveZip(), n.ArchiveDir())
}

func (n *Android) Clean() {
	y.Exec("rm", "-rf", n.ArchiveDir(), n.ArchiveZip())
}

func (n *Android) Reset() {
	// do nothing
}
<commit_msg>設定の有効時のみ処理を行う<commit_after>package buildrtc

import (
	"fmt"
	y "github.com/shiguredo/yspata"
	"io"
	"os"
)

type Android struct {
	Conf *Config
}

func NewAndroid(conf *Config) *Android {
	return &Android{conf}
}

func (n *Android) ArchiveDir() string {
	return fmt.Sprintf("sora-webrtc-%s-android", n.Conf.WebRTCVersion())
}

func (n *Android) ArchiveZip() string {
	return n.ArchiveDir() + ".zip"
}

func (n *Android) Solutions() string {
	return "solutions = [\n" +
		"  {\n" +
		"    \"url\": \"https://webrtc.googlesource.com/src.git\",\n" +
		"    \"managed\": False,\n" +
		"    \"name\": \"src\",\n" +
		"    \"deps_file\": \"DEPS\",\n" +
		"    \"custom_deps\": {},\n" +
		"  },\n" +
		"]\n" +
		"target_os = [\"android\", \"unix\"]\n"
}

func (n *Android) Build() {
	if n.Conf.Debug {
		n.BuildAAR("debug")
	}
	if n.Conf.Release {
		n.BuildAAR("release")
	}
}

func (n *Android) BuildAAR(conf string) {
	y.Printf("Build Android AAR for %s...", conf)

	wd, _ := os.Getwd()
	os.Chdir(n.Conf.WebRTCSrcDir)
	bldDir := y.Join(n.Conf.BuildDir, fmt.Sprintf("android-%s", conf))
	tempDir := y.Join(bldDir, "build")
	libaar := y.Join(bldDir, n.Conf.AndroidAAR)
	y.Execf("mkdir -p %s", bldDir)

	args := []string{n.Conf.Python, n.Conf.AndroidBuildScript,
		"--output", libaar, "--build-dir", tempDir,
		"--build_config", conf, "--arch"}
	if n.Conf.AndroidArchV7A {
		args = append(args, "armeabi-v7a")
	}
	if n.Conf.AndroidArchV8A {
		args = append(args, "arm64-v8a")
	}
	cmd := y.Command("time", args...)
	cmd.OnStdin = func(w io.WriteCloser) {
		io.WriteString(w, "y\n")
	}
	cmd.Run().FailIf("build failed")

	os.Chdir(wd)
}

func (n *Android) Archive() {
	bldDir := n.Conf.BuildDir
	distDir := n.Conf.DistDir
	distDirDg := y.Join(distDir, "android-debug")
	distDirRl := y.Join(distDir, "android-release")

	// clean
	y.Exec("rm", "-rf", distDir, n.ArchiveDir(), n.ArchiveZip())
	y.Exec("mkdir", distDir)
	y.Exec("mkdir", distDirDg)
	y.Exec("mkdir", distDirRl)

	// library
	if n.Conf.Debug {
		y.Exec("cp", y.Join(bldDir, "android-debug", n.Conf.AndroidAAR), distDirDg)
	}
	if n.Conf.Release {
		y.Exec("cp", y.Join(bldDir, "android-release", n.Conf.AndroidAAR), distDirRl)
	}

	// archive
	y.Exec("mv", distDir, n.ArchiveDir())
	y.Exec("zip", "-rq", n.ArchiveZip(), n.ArchiveDir())
}

func (n *Android) Clean() {
	y.Exec("rm", "-rf", n.ArchiveDir(), n.ArchiveZip())
}

func (n *Android) Reset() {
	// do nothing
}
<|endoftext|>


<commit_before>package pongo2

import (
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func init() {
	RegisterFilter("escape", filterEscape)
	RegisterFilter("safe", filterSafe)

	RegisterFilter("add", filterAdd)
	RegisterFilter("capfirst", filterCapfirst)
	RegisterFilter("cut", filterCut)
	RegisterFilter("date", filterDate)
	RegisterFilter("default", filterDefault)
	RegisterFilter("default_if_none", filterDefaultIfNone)
	RegisterFilter("divisibleby", filterDivisibleby)
	RegisterFilter("first", filterFirst)
	RegisterFilter("floatformat", filterFloatformat)
	RegisterFilter("last", filterLast)
	RegisterFilter("length", filterLength)
	RegisterFilter("length_is", filterLengthis)
	RegisterFilter("linebreaksbr", filterLinebreaksbr)
	RegisterFilter("lower", filterLower)
	RegisterFilter("pluralize", filterPluralize)
	RegisterFilter("removetags", filterRemovetags)
	RegisterFilter("upper", filterUpper)
	RegisterFilter("urlencode", filterUrlencode)
	RegisterFilter("striptags", filterStriptags)
	RegisterFilter("time", filterDate) // time uses filterDate (same golang-format)
	RegisterFilter("truncatechars", filterTruncatechars)
	RegisterFilter("yesno", filterYesno)

	RegisterFilter("float", filterFloat)     // pongo-specific
	RegisterFilter("integer", filterInteger) // pongo-specific

	/* Missing filters:

	   addslashes
	   center
	   dictsort
	   dictsortreversed
	   escape
	   escapejs
	   filesizeformat
	   force_escape
	   get_digit
	   iriencode
	   join
	   linebreaks
	   linenumbers
	   ljust
	   make_list
	   phone2numeric
	   pprint
	   random
	   removetags
	   rjust
	   safeseq
	   slice
	   slugify
	   stringformat
	   timesince
	   timeuntil
	   title
	   truncatechars_html
	   truncatewords
	   truncatewords_html
	   unordered_list
	   urlize
	   urlizetrunc
	   wordcount
	   wordwrap

	   Filters that won't be added:

	   static
	   get_static_prefix
	*/
}

func filterTruncatechars(in *Value, param *Value) (*Value, error) {
	s := in.String()
	newLen := param.Integer()
	if newLen < len(s) {
		if newLen >= 3 {
			return AsValue(fmt.Sprintf("%s...", s[:newLen-3])), nil
		}
		// Not enough space for the ellipsis
		return AsValue(s[:newLen]), nil
	}
	return in, nil
}

func filterEscape(in *Value, param *Value) (*Value, error) {
	output := strings.Replace(in.String(), "&", "&", -1)
	output = strings.Replace(output, ">", ">", -1)
	output = strings.Replace(output, "<", "<", -1)
	output = strings.Replace(output, "\"", """, -1)
	output = strings.Replace(output, "'", "'", -1)
	return AsValue(output), nil
}

func filterSafe(in *Value, param *Value) (*Value, error) {
	return in, nil // nothing to do here, just to keep track of the safe application
}

func filterAdd(in *Value, param *Value) (*Value, error) {
	if in.IsNumber() && param.IsNumber() {
		if in.IsFloat() || param.IsFloat() {
			return AsValue(in.Float() + param.Float()), nil
		} else {
			return AsValue(in.Integer() + param.Integer()), nil
		}
	}
	// If in/param is not a number, we're relying on the
	// Value's String() convertion and just add them both together
	return AsValue(in.String() + param.String()), nil
}

func filterCut(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Replace(in.String(), param.String(), "", -1)), nil
}

func filterLength(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Len()), nil
}

func filterLengthis(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Len() == param.Integer()), nil
}

func filterDefault(in *Value, param *Value) (*Value, error) {
	if !in.IsTrue() {
		return param, nil
	}
	return in, nil
}

func filterDefaultIfNone(in *Value, param *Value) (*Value, error) {
	if in.IsNil() {
		return param, nil
	}
	return in, nil
}

func filterDivisibleby(in *Value, param *Value) (*Value, error) {
	if param.Integer() == 0 {
		return AsValue(false), nil
	}
	return AsValue(in.Integer()%param.Integer() == 0), nil
}

func filterFirst(in *Value, param *Value) (*Value, error) {
	if in.CanSlice() {
		return in.Slice(0, 1), nil
	}
	return AsValue(""), nil
}

func filterFloatformat(in *Value, param *Value) (*Value, error) {
	val := in.Float()

	decimals := -1
	if !param.IsNil() {
		// Any argument provided?
		decimals = param.Integer()
	}

	// if the argument is not a number (e. g. empty), the default
	// behaviour is trim the result
	trim := !param.IsNumber()

	if decimals <= 0 {
		// argument is negative or zero, so we
		// want the output being trimmed
		decimals = -decimals
		trim = true
	}

	if trim {
		// Remove zeroes
		if float64(int(val)) == val {
			return AsValue(in.Integer()), nil
		}
	}

	return AsValue(strconv.FormatFloat(val, 'f', decimals, 64)), nil
}

func filterLast(in *Value, param *Value) (*Value, error) {
	if in.CanSlice() {
		return in.Slice(in.Len()-1, in.Len()), nil
	}
	return AsValue(""), nil
}

func filterUpper(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.ToUpper(in.String())), nil
}

func filterLower(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.ToLower(in.String())), nil
}

func filterCapfirst(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Title(in.String())), nil
}

func filterDate(in *Value, param *Value) (*Value, error) {
	t, is_time := in.Interface().(time.Time)
	if !is_time {
		return nil, errors.New("Filter input argument must be of type 'time.Time'.")
	}
	return AsValue(t.Format(param.String())), nil
}

func filterFloat(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Float()), nil
}

func filterInteger(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Integer()), nil
}

func filterLinebreaksbr(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Replace(in.String(), "\n", "
", -1)), nil
}

func filterUrlencode(in *Value, param *Value) (*Value, error) {
	return AsValue(url.QueryEscape(in.String())), nil
}

var re_striptags = regexp.MustCompile("<[^>]*?>")

func filterStriptags(in *Value, param *Value) (*Value, error) {
	s := in.String()

	// Strip all tags
	s = re_striptags.ReplaceAllString(s, "")

	return AsValue(strings.TrimSpace(s)), nil
}

func filterPluralize(in *Value, param *Value) (*Value, error) {
	if in.IsNumber() {
		// Works only on numbers
		if param.Len() > 0 {
			endings := strings.Split(param.String(), ",")
			if len(endings) > 2 {
				return nil, errors.New("You cannot pass more than 2 arguments to filter 'pluralize'.")
			}
			if len(endings) == 1 {
				// 1 argument
				if in.Integer() != 1 {
					return AsValue(endings[0]), nil
				}
			} else {
				if in.Integer() != 1 {
					// 2 arguments
					return AsValue(endings[1]), nil
				}
				return AsValue(endings[0]), nil
			}
		} else {
			if in.Integer() != 1 {
				// return default 's'
				return AsValue("s"), nil
			}
		}

		return AsValue(""), nil
	} else {
		return nil, errors.New("Filter 'pluralize' does only work on numbers.")
	}
}

func filterRemovetags(in *Value, param *Value) (*Value, error) {
	s := in.String()
	tags := strings.Split(param.String(), ",")

	// Strip only specific tags
	for _, tag := range tags {
		re := regexp.MustCompile(fmt.Sprintf("</?%s/?>", tag))
		s = re.ReplaceAllString(s, "")
	}

	return AsValue(strings.TrimSpace(s)), nil
}

func filterYesno(in *Value, param *Value) (*Value, error) {
	choices := map[int]string{
		0: "yes",
		1: "no",
		2: "maybe",
	}
	param_string := param.String()
	custom_choices := strings.Split(param_string, ",")
	if len(param_string) > 0 {
		if len(custom_choices) > 3 {
			return nil, errors.New(fmt.Sprintf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", param_string))
		}
		if len(custom_choices) < 2 {
			return nil, errors.New(fmt.Sprintf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", param_string))
		}

		// Map to the options now
		choices[0] = custom_choices[0]
		choices[1] = custom_choices[1]
		if len(custom_choices) == 3 {
			choices[2] = custom_choices[2]
		}
	}

	// maybe
	if in.IsNil() {
		return AsValue(choices[2]), nil
	}

	// yes
	if in.IsTrue() {
		return AsValue(choices[0]), nil
	}

	// no
	return AsValue(choices[1]), nil
}
<commit_msg>removed escape filter from the missing list.<commit_after>package pongo2

import (
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func init() {
	RegisterFilter("escape", filterEscape)
	RegisterFilter("safe", filterSafe)

	RegisterFilter("add", filterAdd)
	RegisterFilter("capfirst", filterCapfirst)
	RegisterFilter("cut", filterCut)
	RegisterFilter("date", filterDate)
	RegisterFilter("default", filterDefault)
	RegisterFilter("default_if_none", filterDefaultIfNone)
	RegisterFilter("divisibleby", filterDivisibleby)
	RegisterFilter("first", filterFirst)
	RegisterFilter("floatformat", filterFloatformat)
	RegisterFilter("last", filterLast)
	RegisterFilter("length", filterLength)
	RegisterFilter("length_is", filterLengthis)
	RegisterFilter("linebreaksbr", filterLinebreaksbr)
	RegisterFilter("lower", filterLower)
	RegisterFilter("pluralize", filterPluralize)
	RegisterFilter("removetags", filterRemovetags)
	RegisterFilter("upper", filterUpper)
	RegisterFilter("urlencode", filterUrlencode)
	RegisterFilter("striptags", filterStriptags)
	RegisterFilter("time", filterDate) // time uses filterDate (same golang-format)
	RegisterFilter("truncatechars", filterTruncatechars)
	RegisterFilter("yesno", filterYesno)

	RegisterFilter("float", filterFloat)     // pongo-specific
	RegisterFilter("integer", filterInteger) // pongo-specific

	/* Missing filters:

	   addslashes
	   center
	   dictsort
	   dictsortreversed
	   escapejs
	   filesizeformat
	   force_escape
	   get_digit
	   iriencode
	   join
	   linebreaks
	   linenumbers
	   ljust
	   make_list
	   phone2numeric
	   pprint
	   random
	   removetags
	   rjust
	   safeseq
	   slice
	   slugify
	   stringformat
	   timesince
	   timeuntil
	   title
	   truncatechars_html
	   truncatewords
	   truncatewords_html
	   unordered_list
	   urlize
	   urlizetrunc
	   wordcount
	   wordwrap

	   Filters that won't be added:

	   static
	   get_static_prefix
	*/
}

func filterTruncatechars(in *Value, param *Value) (*Value, error) {
	s := in.String()
	newLen := param.Integer()
	if newLen < len(s) {
		if newLen >= 3 {
			return AsValue(fmt.Sprintf("%s...", s[:newLen-3])), nil
		}
		// Not enough space for the ellipsis
		return AsValue(s[:newLen]), nil
	}
	return in, nil
}

func filterEscape(in *Value, param *Value) (*Value, error) {
	output := strings.Replace(in.String(), "&", "&", -1)
	output = strings.Replace(output, ">", ">", -1)
	output = strings.Replace(output, "<", "<", -1)
	output = strings.Replace(output, "\"", """, -1)
	output = strings.Replace(output, "'", "'", -1)
	return AsValue(output), nil
}

func filterSafe(in *Value, param *Value) (*Value, error) {
	return in, nil // nothing to do here, just to keep track of the safe application
}

func filterAdd(in *Value, param *Value) (*Value, error) {
	if in.IsNumber() && param.IsNumber() {
		if in.IsFloat() || param.IsFloat() {
			return AsValue(in.Float() + param.Float()), nil
		} else {
			return AsValue(in.Integer() + param.Integer()), nil
		}
	}
	// If in/param is not a number, we're relying on the
	// Value's String() convertion and just add them both together
	return AsValue(in.String() + param.String()), nil
}

func filterCut(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Replace(in.String(), param.String(), "", -1)), nil
}

func filterLength(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Len()), nil
}

func filterLengthis(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Len() == param.Integer()), nil
}

func filterDefault(in *Value, param *Value) (*Value, error) {
	if !in.IsTrue() {
		return param, nil
	}
	return in, nil
}

func filterDefaultIfNone(in *Value, param *Value) (*Value, error) {
	if in.IsNil() {
		return param, nil
	}
	return in, nil
}

func filterDivisibleby(in *Value, param *Value) (*Value, error) {
	if param.Integer() == 0 {
		return AsValue(false), nil
	}
	return AsValue(in.Integer()%param.Integer() == 0), nil
}

func filterFirst(in *Value, param *Value) (*Value, error) {
	if in.CanSlice() {
		return in.Slice(0, 1), nil
	}
	return AsValue(""), nil
}

func filterFloatformat(in *Value, param *Value) (*Value, error) {
	val := in.Float()

	decimals := -1
	if !param.IsNil() {
		// Any argument provided?
		decimals = param.Integer()
	}

	// if the argument is not a number (e. g. empty), the default
	// behaviour is trim the result
	trim := !param.IsNumber()

	if decimals <= 0 {
		// argument is negative or zero, so we
		// want the output being trimmed
		decimals = -decimals
		trim = true
	}

	if trim {
		// Remove zeroes
		if float64(int(val)) == val {
			return AsValue(in.Integer()), nil
		}
	}

	return AsValue(strconv.FormatFloat(val, 'f', decimals, 64)), nil
}

func filterLast(in *Value, param *Value) (*Value, error) {
	if in.CanSlice() {
		return in.Slice(in.Len()-1, in.Len()), nil
	}
	return AsValue(""), nil
}

func filterUpper(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.ToUpper(in.String())), nil
}

func filterLower(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.ToLower(in.String())), nil
}

func filterCapfirst(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Title(in.String())), nil
}

func filterDate(in *Value, param *Value) (*Value, error) {
	t, is_time := in.Interface().(time.Time)
	if !is_time {
		return nil, errors.New("Filter input argument must be of type 'time.Time'.")
	}
	return AsValue(t.Format(param.String())), nil
}

func filterFloat(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Float()), nil
}

func filterInteger(in *Value, param *Value) (*Value, error) {
	return AsValue(in.Integer()), nil
}

func filterLinebreaksbr(in *Value, param *Value) (*Value, error) {
	return AsValue(strings.Replace(in.String(), "\n", "
", -1)), nil
}

func filterUrlencode(in *Value, param *Value) (*Value, error) {
	return AsValue(url.QueryEscape(in.String())), nil
}

var re_striptags = regexp.MustCompile("<[^>]*?>")

func filterStriptags(in *Value, param *Value) (*Value, error) {
	s := in.String()

	// Strip all tags
	s = re_striptags.ReplaceAllString(s, "")

	return AsValue(strings.TrimSpace(s)), nil
}

func filterPluralize(in *Value, param *Value) (*Value, error) {
	if in.IsNumber() {
		// Works only on numbers
		if param.Len() > 0 {
			endings := strings.Split(param.String(), ",")
			if len(endings) > 2 {
				return nil, errors.New("You cannot pass more than 2 arguments to filter 'pluralize'.")
			}
			if len(endings) == 1 {
				// 1 argument
				if in.Integer() != 1 {
					return AsValue(endings[0]), nil
				}
			} else {
				if in.Integer() != 1 {
					// 2 arguments
					return AsValue(endings[1]), nil
				}
				return AsValue(endings[0]), nil
			}
		} else {
			if in.Integer() != 1 {
				// return default 's'
				return AsValue("s"), nil
			}
		}

		return AsValue(""), nil
	} else {
		return nil, errors.New("Filter 'pluralize' does only work on numbers.")
	}
}

func filterRemovetags(in *Value, param *Value) (*Value, error) {
	s := in.String()
	tags := strings.Split(param.String(), ",")

	// Strip only specific tags
	for _, tag := range tags {
		re := regexp.MustCompile(fmt.Sprintf("</?%s/?>", tag))
		s = re.ReplaceAllString(s, "")
	}

	return AsValue(strings.TrimSpace(s)), nil
}

func filterYesno(in *Value, param *Value) (*Value, error) {
	choices := map[int]string{
		0: "yes",
		1: "no",
		2: "maybe",
	}
	param_string := param.String()
	custom_choices := strings.Split(param_string, ",")
	if len(param_string) > 0 {
		if len(custom_choices) > 3 {
			return nil, errors.New(fmt.Sprintf("You cannot pass more than 3 options to the 'yesno'-filter (got: '%s').", param_string))
		}
		if len(custom_choices) < 2 {
			return nil, errors.New(fmt.Sprintf("You must pass either no or at least 2 arguments to the 'yesno'-filter (got: '%s').", param_string))
		}

		// Map to the options now
		choices[0] = custom_choices[0]
		choices[1] = custom_choices[1]
		if len(custom_choices) == 3 {
			choices[2] = custom_choices[2]
		}
	}

	// maybe
	if in.IsNil() {
		return AsValue(choices[2]), nil
	}

	// yes
	if in.IsTrue() {
		return AsValue(choices[0]), nil
	}

	// no
	return AsValue(choices[1]), nil
}
<|endoftext|>


<commit_before>package lfufdcache

import (
	"io/ioutil"
	"os"
	"sync"

	"testing"
)

func TestNoopReadFailsOnClosed(t *testing.T) {
	fd, err := ioutil.TempFile("", "fdcache")
	if err != nil {
		t.Fatal(err)
		return
	}
	fd.WriteString("test")
	fd.Close()
	buf := make([]byte, 4)
	_, err = fd.ReadAt(buf, 0)
	if err == nil {
		t.Fatal("Expected error")
	}
}

func TestSingleFileEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}

	fd, err := ioutil.TempFile("", "fdcache")
	if err != nil {
		t.Fatal(err)
		return
	}
	fd.WriteString("test")
	fd.Close()
	buf := make([]byte, 4)

	for k := 0; k < 100; k++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			cfd, err := c.Open(fd.Name())
			if err != nil {
				t.Fatal(err)
				return
			}
			defer cfd.Close()

			_, err = cfd.ReadAt(buf, 0)
			if err != nil {
				t.Fatal(err)
			}
		}()
	}

	wg.Wait()
}

func TestMultifileEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}

	for k := 0; k < 100; k++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			fd, err := ioutil.TempFile("", "fdcache")
			if err != nil {
				t.Fatal(err)
				return
			}
			fd.WriteString("test")
			fd.Close()
			buf := make([]byte, 4)
			defer os.Remove(fd.Name())

			cfd, err := c.Open(fd.Name())
			if err != nil {
				t.Fatal(err)
				return
			}
			defer cfd.Close()

			_, err = cfd.ReadAt(buf, 0)
			if err != nil {
				t.Fatal(err)
			}
		}()
	}

	wg.Wait()
}

func TestMixedEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		go func() {
			fd, err := ioutil.TempFile("", "fdcache")
			if err != nil {
				t.Fatal(err)
				return
			}
			fd.WriteString("test")
			fd.Close()
			buf := make([]byte, 4)

			for k := 0; k < 100; k++ {
				wg.Add(1)
				go func() {
					defer wg.Done()

					cfd, err := c.Open(fd.Name())
					if err != nil {
						t.Fatal(err)
						return
					}
					defer cfd.Close()

					_, err = cfd.ReadAt(buf, 0)
					if err != nil {
						t.Fatal(err)
					}
				}()
			}
		}()
	}

	wg.Wait()
}
<commit_msg>Add test case<commit_after>package lfufdcache

import (
	"io/ioutil"
	"os"
	"sync"
	"time"

	"testing"
)

func TestNoopReadFailsOnClosed(t *testing.T) {
	fd, err := ioutil.TempFile("", "fdcache")
	if err != nil {
		t.Fatal(err)
		return
	}
	fd.WriteString("test")
	fd.Close()
	buf := make([]byte, 4)
	defer os.Remove(fd.Name())

	_, err = fd.ReadAt(buf, 0)
	if err == nil {
		t.Fatal("Expected error")
	}
}

func TestSingleFileEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}

	fd, err := ioutil.TempFile("", "fdcache")
	if err != nil {
		t.Fatal(err)
		return
	}
	fd.WriteString("test")
	fd.Close()
	buf := make([]byte, 4)
	defer os.Remove(fd.Name())

	for k := 0; k < 100; k++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			cfd, err := c.Open(fd.Name())
			if err != nil {
				t.Fatal(err)
				return
			}
			defer cfd.Close()

			_, err = cfd.ReadAt(buf, 0)
			if err != nil {
				t.Fatal(err)
			}
		}()
	}

	wg.Wait()
}

func TestMultifileEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}

	for k := 0; k < 100; k++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			fd, err := ioutil.TempFile("", "fdcache")
			if err != nil {
				t.Fatal(err)
				return
			}
			fd.WriteString("test")
			fd.Close()
			buf := make([]byte, 4)
			defer os.Remove(fd.Name())

			cfd, err := c.Open(fd.Name())
			if err != nil {
				t.Fatal(err)
				return
			}
			defer cfd.Close()

			_, err = cfd.ReadAt(buf, 0)
			if err != nil {
				t.Fatal(err)
			}
		}()
	}

	wg.Wait()
}

func TestMixedEviction(t *testing.T) {
	c := NewCache(1, 1)

	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		go func() {
			fd, err := ioutil.TempFile("", "fdcache")
			if err != nil {
				t.Fatal(err)
				return
			}
			fd.WriteString("test")
			fd.Close()
			buf := make([]byte, 4)

			for k := 0; k < 100; k++ {
				wg.Add(1)
				go func() {
					defer wg.Done()

					cfd, err := c.Open(fd.Name())
					if err != nil {
						t.Fatal(err)
						return
					}
					defer cfd.Close()

					_, err = cfd.ReadAt(buf, 0)
					if err != nil {
						t.Fatal(err)
					}
				}()
			}
		}()
	}

	wg.Wait()
}

func TestLimit(t *testing.T) {
	testcase := 50
	fd, err := ioutil.TempFile("", "fdcache")
	if err != nil {
		t.Fatal(err)
		return
	}
	fd.Close()
	defer os.Remove(fd.Name())

	c := NewCache(testcase/5, testcase)
	fds := make([]*CachedFile, testcase*2)
	for i := 0; i < testcase*2; i++ {
		fd, err := ioutil.TempFile("", "fdcache")
		if err != nil {
			t.Fatal(err)
			return
		}
		fd.WriteString("test")
		fd.Close()
		defer os.Remove(fd.Name())

		nfd, err := c.Open(fd.Name())
		if err != nil {
			t.Fatal(err)
			return
		}
		fds = append(fds, nfd)
		nfd.Close()
	}

	// Allow closes to happen
	time.Sleep(time.Millisecond * 100)

	buf := make([]byte, 4)
	ok := 0
	for _, fd := range fds {
		if fd == nil {
			continue
		}
		_, err := fd.ReadAt(buf, 0)
		if err == nil {
			ok++
		}
	}
	if ok > testcase {
		t.Fatal("More than", testcase, "fds open")
	}
}
<|endoftext|>


<commit_before>// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package ghw

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

func cachesForNode(nodeID int) ([]*MemoryCache, error) {
	// The /sys/devices/node/nodeX directory contains a subdirectory called
	// 'cpuX' for each logical processor assigned to the node. Each of those
	// subdirectories containers a 'cache' subdirectory which contains a number
	// of subdirectories beginning with 'index' and ending in the cache's
	// internal 0-based identifier. Those subdirectories contain a number of
	// files, including 'shared_cpu_list', 'size', and 'type' which we use to
	// determine cache characteristics.
	path := filepath.Join(
		pathSysDevicesSystemNode(),
		fmt.Sprintf("node%d", nodeID),
	)
	caches := make(map[string]*MemoryCache, 0)

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		filename := file.Name()
		if !strings.HasPrefix(filename, "cpu") {
			continue
		}
		if filename == "cpumap" || filename == "cpulist" {
			// There are two files in the node directory that start with 'cpu'
			// but are not subdirectories ('cpulist' and 'cpumap'). Ignore
			// these files.
			continue
		}
		// Grab the logical processor ID by cutting the integer from the
		// /sys/devices/system/node/nodeX/cpuX filename
		cpuPath := filepath.Join(path, filename)
		lpID, _ := strconv.Atoi(filename[3:])

		// Inspect the caches for each logical processor. There will be a
		// /sys/devices/system/node/nodeX/cpuX/cache directory containing a
		// number of directories beginning with the prefix "index" followed by
		// a number. The number indicates the level of the cache, which
		// indicates the "distance" from the processor. Each of these
		// directories contains information about the size of that level of
		// cache and the processors mapped to it.
		cachePath := filepath.Join(cpuPath, "cache")
		cacheDirFiles, err := ioutil.ReadDir(cachePath)
		if err != nil {
			return nil, err
		}
		for _, cacheDirFile := range cacheDirFiles {
			cacheDirFileName := cacheDirFile.Name()
			if !strings.HasPrefix(cacheDirFileName, "index") {
				continue
			}

			typePath := filepath.Join(cachePath, cacheDirFileName, "type")
			cacheTypeContents, err := ioutil.ReadFile(typePath)
			if err != nil {
				continue
			}
			var cacheType MemoryCacheType
			switch string(cacheTypeContents[:len(cacheTypeContents)-1]) {
			case "Data":
				cacheType = DATA
			case "Instruction":
				cacheType = INSTRUCTION
			default:
				cacheType = UNIFIED
			}

			levelPath := filepath.Join(cachePath, cacheDirFileName, "level")
			levelContents, err := ioutil.ReadFile(levelPath)
			if err != nil {
				continue
			}
			// levelContents is now a []byte with the last byte being a newline
			// character. Trim that off and convert the contents to an integer.
			level, _ := strconv.Atoi(string(levelContents[:len(levelContents)-1]))

			size := memoryCacheSize(nodeID, lpID, level)

			scpuPath := filepath.Join(
				cachePath,
				cacheDirFileName,
				"shared_cpu_map",
			)
			sharedCpuMap, err := ioutil.ReadFile(scpuPath)
			if err != nil {
				continue
			}
			// The cache information is repeated for each node, so here, we
			// just ensure that we only have a one MemoryCache object for each
			// unique combination of level, type and processor map
			cacheKey := fmt.Sprintf("%d-%d-%s", level, cacheType, sharedCpuMap[:len(sharedCpuMap)-1])
			cache, exists := caches[cacheKey]
			if !exists {
				cache = &MemoryCache{
					Level:             uint8(level),
					Type:              cacheType,
					SizeBytes:         uint64(size) * uint64(KB),
					LogicalProcessors: make([]uint32, 0),
				}
				caches[cacheKey] = cache
			}
			cache.LogicalProcessors = append(
				cache.LogicalProcessors,
				uint32(lpID),
			)
		}
	}

	cacheVals := make([]*MemoryCache, len(caches))
	x := 0
	for _, c := range caches {
		// ensure the cache's processor set is sorted by logical process ID
		sort.Sort(SortByLogicalProcessorId(c.LogicalProcessors))
		cacheVals[x] = c
		x++
	}

	return cacheVals, nil
}

func pathNodeCPU(nodeID int, lpID int) string {
	return filepath.Join(
		pathSysDevicesSystemNode(),
		fmt.Sprintf("node%d", nodeID),
		fmt.Sprintf("cpu%d", lpID),
	)
}

func pathNodeCPUCache(nodeID int, lpID int) string {
	return filepath.Join(
		pathNodeCPU(nodeID, lpID),
		"cache",
	)
}

func pathNodeCPUCacheLevel(nodeID int, lpID int, cacheLevel int) string {
	return filepath.Join(
		pathNodeCPUCache(nodeID, lpID),
		fmt.Sprintf("index%d", cacheLevel),
	)
}

func memoryCacheSize(nodeID int, lpID int, cacheLevel int) int {
	sizePath := filepath.Join(
		pathNodeCPUCacheLevel(nodeID, lpID, cacheLevel),
		"size",
	)
	sizeContents, err := ioutil.ReadFile(sizePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read %s: %s", sizePath, err)
		return 0
	}
	// size comes as XK\n, so we trim off the K and the newline.
	size, err := strconv.Atoi(string(sizeContents[:len(sizeContents)-2]))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to parse int from %s", sizeContents)
		return 0
	}
	return size
}
<commit_msg>lint-fixes: Pull cache level getter into sep func<commit_after>// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package ghw

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

func cachesForNode(nodeID int) ([]*MemoryCache, error) {
	// The /sys/devices/node/nodeX directory contains a subdirectory called
	// 'cpuX' for each logical processor assigned to the node. Each of those
	// subdirectories containers a 'cache' subdirectory which contains a number
	// of subdirectories beginning with 'index' and ending in the cache's
	// internal 0-based identifier. Those subdirectories contain a number of
	// files, including 'shared_cpu_list', 'size', and 'type' which we use to
	// determine cache characteristics.
	path := filepath.Join(
		pathSysDevicesSystemNode(),
		fmt.Sprintf("node%d", nodeID),
	)
	caches := make(map[string]*MemoryCache, 0)

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		filename := file.Name()
		if !strings.HasPrefix(filename, "cpu") {
			continue
		}
		if filename == "cpumap" || filename == "cpulist" {
			// There are two files in the node directory that start with 'cpu'
			// but are not subdirectories ('cpulist' and 'cpumap'). Ignore
			// these files.
			continue
		}
		// Grab the logical processor ID by cutting the integer from the
		// /sys/devices/system/node/nodeX/cpuX filename
		cpuPath := filepath.Join(path, filename)
		lpID, _ := strconv.Atoi(filename[3:])

		// Inspect the caches for each logical processor. There will be a
		// /sys/devices/system/node/nodeX/cpuX/cache directory containing a
		// number of directories beginning with the prefix "index" followed by
		// a number. The number indicates the level of the cache, which
		// indicates the "distance" from the processor. Each of these
		// directories contains information about the size of that level of
		// cache and the processors mapped to it.
		cachePath := filepath.Join(cpuPath, "cache")
		cacheDirFiles, err := ioutil.ReadDir(cachePath)
		if err != nil {
			return nil, err
		}
		for _, cacheDirFile := range cacheDirFiles {
			cacheDirFileName := cacheDirFile.Name()
			if !strings.HasPrefix(cacheDirFileName, "index") {
				continue
			}

			typePath := filepath.Join(cachePath, cacheDirFileName, "type")
			cacheTypeContents, err := ioutil.ReadFile(typePath)
			if err != nil {
				continue
			}
			var cacheType MemoryCacheType
			switch string(cacheTypeContents[:len(cacheTypeContents)-1]) {
			case "Data":
				cacheType = DATA
			case "Instruction":
				cacheType = INSTRUCTION
			default:
				cacheType = UNIFIED
			}
			level := memoryCacheLevel(nodeID, lpID)
			size := memoryCacheSize(nodeID, lpID, level)

			scpuPath := filepath.Join(
				cachePath,
				cacheDirFileName,
				"shared_cpu_map",
			)
			sharedCpuMap, err := ioutil.ReadFile(scpuPath)
			if err != nil {
				continue
			}
			// The cache information is repeated for each node, so here, we
			// just ensure that we only have a one MemoryCache object for each
			// unique combination of level, type and processor map
			cacheKey := fmt.Sprintf("%d-%d-%s", level, cacheType, sharedCpuMap[:len(sharedCpuMap)-1])
			cache, exists := caches[cacheKey]
			if !exists {
				cache = &MemoryCache{
					Level:             uint8(level),
					Type:              cacheType,
					SizeBytes:         uint64(size) * uint64(KB),
					LogicalProcessors: make([]uint32, 0),
				}
				caches[cacheKey] = cache
			}
			cache.LogicalProcessors = append(
				cache.LogicalProcessors,
				uint32(lpID),
			)
		}
	}

	cacheVals := make([]*MemoryCache, len(caches))
	x := 0
	for _, c := range caches {
		// ensure the cache's processor set is sorted by logical process ID
		sort.Sort(SortByLogicalProcessorId(c.LogicalProcessors))
		cacheVals[x] = c
		x++
	}

	return cacheVals, nil
}

func pathNodeCPU(nodeID int, lpID int) string {
	return filepath.Join(
		pathSysDevicesSystemNode(),
		fmt.Sprintf("node%d", nodeID),
		fmt.Sprintf("cpu%d", lpID),
	)
}

func pathNodeCPUCache(nodeID int, lpID int) string {
	return filepath.Join(
		pathNodeCPU(nodeID, lpID),
		"cache",
	)
}

func pathNodeCPUCacheLevel(nodeID int, lpID int, cacheLevel int) string {
	return filepath.Join(
		pathNodeCPUCache(nodeID, lpID),
		fmt.Sprintf("index%d", cacheLevel),
	)
}

func memoryCacheSize(nodeID int, lpID int, cacheLevel int) int {
	sizePath := filepath.Join(
		pathNodeCPUCacheLevel(nodeID, lpID, cacheLevel),
		"size",
	)
	sizeContents, err := ioutil.ReadFile(sizePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read %s: %s", sizePath, err)
		return -1
	}
	// size comes as XK\n, so we trim off the K and the newline.
	size, err := strconv.Atoi(string(sizeContents[:len(sizeContents)-2]))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to parse int from %s", sizeContents)
		return -1
	}
	return size
}

func memoryCacheLevel(nodeID int, lpID int) int {
	levelPath := filepath.Join(
		pathNodeCPUCache(nodeID, lpID),
		"level",
	)
	levelContents, err := ioutil.ReadFile(levelPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read %s: %s", levelPath, err)
		return -1
	}
	// levelContents is now a []byte with the last byte being a newline
	// character. Trim that off and convert the contents to an integer.
	level, err := strconv.Atoi(string(levelContents[:len(levelContents)-1]))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to parse int from %s", levelContents)
		return -1
	}
	return level
}
<|endoftext|>


<commit_before>package caddy

import (
	"net/http"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"

	"github.com/txtdirect/txtdirect"
)

func init() {
	caddy.RegisterPlugin("txtdirect", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

func setup(c *caddy.Controller) error {
	var enable, disable []string
	c.Next() // skip directive name
	for c.NextBlock() {
		option := c.Val()
		switch option {
		case "enable":
			if disable != nil {
				return c.ArgErr()
			}
			enable = c.RemainingArgs()
		case "disable":
			if enable != nil {
				return c.ArgErr()
			}
			disable = c.RemainingArgs()
		default:
			return c.ArgErr() // unhandled option
		}
	}

	// Add handler to Caddy
	cfg := httpserver.GetConfig(c)
	mid := func(next httpserver.Handler) httpserver.Handler {
		return Redirect{
			Next:    next,
			Enable:  enable,
			Disable: disable,
		}
	}
	cfg.AddMiddleware(mid)
	return nil
}

// Redirect is middleware to redirect requests based on TXT records
type Redirect struct {
	Next    httpserver.Handler
	Enable  []string
	Disable []string
}

func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if err := txtdirect.Redirect(w, r); err != nil {
		return http.StatusInternalServerError, err
	}
	return 0, nil
}
<commit_msg>Only use one list for enabled options<commit_after>package caddy

import (
	"net/http"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"

	"github.com/txtdirect/txtdirect"
)

func init() {
	caddy.RegisterPlugin("txtdirect", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

var allOptions = []string{"host", "gometa"}

func setup(c *caddy.Controller) error {
	var enable, disable []string
	c.Next() // skip directive name
	for c.NextBlock() {
		option := c.Val()
		switch option {
		case "enable":
			if disable != nil {
				return c.ArgErr()
			}
			enable = c.RemainingArgs()
		case "disable":
			if enable != nil {
				return c.ArgErr()
			}
			disable = removeArrayFromArray(disable, c.RemainingArgs())
		default:
			return c.ArgErr() // unhandled option
		}
	}

	// If nothing is specified, enable everything
	if disable == nil && enable == nil {
		enable = allOptions
	}

	// Add handler to Caddy
	cfg := httpserver.GetConfig(c)
	mid := func(next httpserver.Handler) httpserver.Handler {
		return Redirect{
			Next:   next,
			Enable: enable,
		}
	}
	cfg.AddMiddleware(mid)
	return nil
}

func removeArrayFromArray(array, toBeRemoved []string) []string {
	for _, toRemove := range toBeRemoved {
		for i, option := range array {
			if option == toRemove {
				array[i] = array[len(array)-1]
				array = array[:len(array)-1]
				break
			}
		}
	}
	return array
}

// Redirect is middleware to redirect requests based on TXT records
type Redirect struct {
	Next   httpserver.Handler
	Enable []string
}

func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if err := txtdirect.Redirect(w, r, rd.Enable); err != nil {
		return http.StatusInternalServerError, err
	}
	return 0, nil
}
<|endoftext|>


<commit_before>package operators

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"github.com/stretchr/objx"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	configv1 "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned"
)

const (
	operatorWait = 1 * time.Minute
	cvoWait      = 5 * time.Minute
)

var _ = g.Describe("[sig-arch][Early] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("start all core operators", func() {
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())
		dc, err := dynamic.NewForConfig(cfg)
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(c.CoreV1().Namespaces())

		g.By("waiting for the cluster version to be applied")
		cvc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusterversions", Version: "v1"})
		var lastErr error
		var lastCV objx.Map
		if err := wait.PollImmediate(3*time.Second, cvoWait, func() (bool, error) {
			obj, err := cvc.Get(context.Background(), "version", metav1.GetOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster version: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			lastCV = cv
			if cond := condition(cv, "Progressing"); cond.Get("status").String() != "False" {
				e2e.Logf("ClusterVersion is still progressing: %s", cond.Get("message").String())
				return false, nil
			}
			if cond := condition(cv, "Available"); cond.Get("status").String() != "True" {
				e2e.Logf("ClusterVersion is not available: %s", cond.Get("message").String())
				return false, nil
			}
			e2e.Logf("ClusterVersion available: %s", condition(cv, "Progressing").Get("message").String())
			return true, nil
		}); err != nil {
			o.Expect(lastErr).NotTo(o.HaveOccurred())
			e2e.Logf("Last cluster version seen: %s", lastCV)
			if msg := condition(lastCV, "Failing").Get("message").String(); len(msg) > 0 {
				e2e.Logf("ClusterVersion is reporting a failure: %s", msg)
			}
			e2e.Failf("ClusterVersion never became available: %s", condition(lastCV, "Progressing").Get("message").String())
		}

		// gate on all clusteroperators being ready
		available := make(map[string]struct{})
		g.By(fmt.Sprintf("waiting for all cluster operators to be stable at the same time"))
		coc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusteroperators", Version: "v1"})
		lastErr = nil
		var lastCOs []objx.Map
		wait.PollImmediate(time.Second, operatorWait, func() (bool, error) {
			obj, err := coc.List(context.Background(), metav1.ListOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster operators: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			items := objects(cv.Get("items"))
			lastCOs = items

			if len(items) == 0 {
				return false, nil
			}

			var unavailable []objx.Map
			var unavailableNames []string
			for _, co := range items {
				if condition(co, "Available").Get("status").String() != "True" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Progressing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
				if condition(co, "Failing").Get("status").String() != "False" {
					ns := co.Get("metadata.namespace").String()
					name := co.Get("metadata.name").String()
					unavailableNames = append(unavailableNames, fmt.Sprintf("%s/%s", ns, name))
					unavailable = append(unavailable, co)
					break
				}
			}
			if len(unavailable) > 0 {
				e2e.Logf("Operators still doing work: %s", strings.Join(unavailableNames, ", "))
				return false, nil
			}
			return true, nil
		})

		o.Expect(lastErr).NotTo(o.HaveOccurred())
		var unavailable []string
		buf := &bytes.Buffer{}
		w := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
		fmt.Fprintf(w, "NAMESPACE\tNAME\tPROGRESSING\tAVAILABLE\tVERSION\tMESSAGE\n")
		for _, co := range lastCOs {
			ns := co.Get("metadata.namespace").String()
			name := co.Get("metadata.name").String()
			if condition(co, "Available").Get("status").String() != "True" {
				unavailable = append(unavailable, fmt.Sprintf("%s/%s", ns, name))
			} else {
				available[fmt.Sprintf("%s/%s", ns, name)] = struct{}{}
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
				ns,
				name,
				condition(co, "Progressing").Get("status").String(),
				condition(co, "Available").Get("status").String(),
				co.Get("status.version").String(),
				condition(co, "Failing").Get("message").String(),
			)
		}
		w.Flush()
		e2e.Logf("ClusterOperators:\n%s", buf.String())

		if len(unavailable) > 0 {
			e2e.Failf("Some cluster operators never became available %s", strings.Join(unavailable, ", "))
		}
		// Check at least one core operator is available
		if len(available) == 0 {
			e2e.Failf("There must be at least one cluster operator")
		}
	})
})

var _ = g.Describe("[sig-arch] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("have operators on the cluster version", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2eskipper.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c := configclient.NewForConfigOrDie(cfg)
		coreclient, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(coreclient.CoreV1().Namespaces())

		// we need to get the list of versions
		cv, err := c.ConfigV1().ClusterVersions().Get(context.Background(), "version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		coList, err := c.ConfigV1().ClusterOperators().List(context.Background(), metav1.ListOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(coList.Items).NotTo(o.BeEmpty())

		g.By("all cluster operators report an operator version in the first position equal to the cluster version")
		for _, co := range coList.Items {
			msg := fmt.Sprintf("unexpected operator status versions %s:\n%#v", co.Name, co.Status.Versions)
			o.Expect(co.Status.Versions).NotTo(o.BeEmpty(), msg)
			operator := findOperatorVersion(co.Status.Versions, "operator")
			o.Expect(operator).NotTo(o.BeNil(), msg)
			o.Expect(operator.Name).To(o.Equal("operator"), msg)
			o.Expect(operator.Version).To(o.Equal(cv.Status.Desired.Version), msg)
		}
	})
})

func skipUnlessCVO(c coreclient.NamespaceInterface) {
	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
		_, err := c.Get(context.Background(), "openshift-cluster-version", metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if errors.IsNotFound(err) {
			e2eskipper.Skipf("The cluster is not managed by a cluster-version operator")
		}
		e2e.Logf("Unable to check for cluster version operator: %v", err)
		return false, nil
	})
	o.Expect(err).NotTo(o.HaveOccurred())
}

func findOperatorVersion(versions []configv1.OperandVersion, name string) *configv1.OperandVersion {
	for i := range versions {
		if versions[i].Name == name {
			return &versions[i]
		}
	}
	return nil
}

func contains(names []string, name string) bool {
	for _, s := range names {
		if s == name {
			return true
		}
	}
	return false
}

func jsonString(from objx.Map) string {
	s, _ := from.JSON()
	return s
}

func objects(from *objx.Value) []objx.Map {
	var values []objx.Map
	switch {
	case from.IsObjxMapSlice():
		return from.ObjxMapSlice()
	case from.IsInterSlice():
		for _, i := range from.InterSlice() {
			if msi, ok := i.(map[string]interface{}); ok {
				values = append(values, objx.Map(msi))
			}
		}
	}
	return values
}

func condition(cv objx.Map, condition string) objx.Map {
	for _, obj := range objects(cv.Get("status.conditions")) {
		if obj.Get("type").String() == condition {
			return obj
		}
	}
	return objx.Map(nil)
}
<commit_msg>test/extended/operators/operators: Rework "start all core operators"<commit_after>package operators

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"github.com/stretchr/objx"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/dynamic"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	configv1 "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned"
)

const (
	operatorWait = 1 * time.Minute
	cvoWait      = 5 * time.Minute
)

var _ = g.Describe("[sig-arch][Early] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("start all core operators", func() {
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())
		dc, err := dynamic.NewForConfig(cfg)
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(c.CoreV1().Namespaces())

		g.By("waiting for the cluster version to be applied")
		cvc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusterversions", Version: "v1"})
		var lastErr error
		var lastCV objx.Map
		if err := wait.PollImmediate(3*time.Second, cvoWait, func() (bool, error) {
			obj, err := cvc.Get(context.Background(), "version", metav1.GetOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster version: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			lastCV = cv
			if cond := condition(cv, "Progressing"); cond.Get("status").String() != "False" {
				e2e.Logf("ClusterVersion is still progressing: %s", cond.Get("message").String())
				return false, nil
			}
			if cond := condition(cv, "Available"); cond.Get("status").String() != "True" {
				e2e.Logf("ClusterVersion is not available: %s", cond.Get("message").String())
				return false, nil
			}
			e2e.Logf("ClusterVersion available: %s", condition(cv, "Progressing").Get("message").String())
			return true, nil
		}); err != nil {
			o.Expect(lastErr).NotTo(o.HaveOccurred())
			e2e.Logf("Last cluster version seen: %s", lastCV)
			if msg := condition(lastCV, "Failing").Get("message").String(); len(msg) > 0 {
				e2e.Logf("ClusterVersion is reporting a failure: %s", msg)
			}
			e2e.Failf("ClusterVersion never became available: %s", condition(lastCV, "Progressing").Get("message").String())
		}

		// gate on all clusteroperators being ready
		g.By(fmt.Sprintf("waiting for all cluster operators to be stable at the same time"))
		coc := dc.Resource(schema.GroupVersionResource{Group: "config.openshift.io", Resource: "clusteroperators", Version: "v1"})
		lastErr = nil
		var lastCOs []objx.Map
		wait.PollImmediate(time.Second, operatorWait, func() (bool, error) {
			obj, err := coc.List(context.Background(), metav1.ListOptions{})
			if err != nil {
				lastErr = err
				e2e.Logf("Unable to check for cluster operators: %v", err)
				return false, nil
			}
			cv := objx.Map(obj.UnstructuredContent())
			lastErr = nil
			items := objects(cv.Get("items"))
			lastCOs = items

			if len(items) == 0 {
				return false, nil
			}

			var unready []string
			for _, co := range items {
				badConditions, missingTypes := surprisingConditions(co)
				if len(badConditions) > 0 || len(missingTypes) > 0 {
					unready = append(unready, co.Get("metadata.name").String())
				}
			}
			if len(unready) > 0 {
				sort.Strings(unready)
				e2e.Logf("Operators still unready: %s", strings.Join(unready, ", "))
				return false, nil
			}
			return true, nil
		})

		o.Expect(lastErr).NotTo(o.HaveOccurred())
		ready := 0
		var unready []string
		buf := &bytes.Buffer{}
		w := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
		fmt.Fprintf(w, "NAME\tTYPE\tSTATUS\tREASON\tMESSAGE\n")
		for _, co := range lastCOs {
			name := co.Get("metadata.name").String()
			badConditions, missingTypes := surprisingConditions(co)
			if len(badConditions) > 0 {
				worstCondition := badConditions[0]
				unready = append(unready, fmt.Sprintf("%s (%s=%s %s: %s)",
					name,
					worstCondition.Type,
					worstCondition.Status,
					worstCondition.Reason,
					worstCondition.Message,
				))
				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
					name,
					worstCondition.Type,
					worstCondition.Status,
					worstCondition.Reason,
					worstCondition.Message,
				)
			} else if len(missingTypes) > 0 {
				missingTypeStrings := make([]string, 0, len(missingTypes))
				for _, missingType := range missingTypes {
					missingTypeStrings = append(missingTypeStrings, string(missingType))
				}
				unready = append(unready, fmt.Sprintf("%s (missing: %s)", name, strings.Join(missingTypeStrings, ", ")))
			} else {
				ready++
			}
		}
		w.Flush()
		e2e.Logf("ClusterOperators:\n%s", buf.String())

		if len(unready) > 0 {
			sort.Strings(unready)
			e2e.Failf("Some cluster operators never became ready: %s", strings.Join(unready, ", "))
		}
		// Check at least one core operator is ready
		if ready == 0 {
			e2e.Failf("There must be at least one cluster operator")
		}
	})
})

var _ = g.Describe("[sig-arch] Managed cluster should", func() {
	defer g.GinkgoRecover()

	g.It("have operators on the cluster version", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2eskipper.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		cfg, err := e2e.LoadConfig()
		o.Expect(err).NotTo(o.HaveOccurred())
		c := configclient.NewForConfigOrDie(cfg)
		coreclient, err := e2e.LoadClientset()
		o.Expect(err).NotTo(o.HaveOccurred())

		// presence of the CVO namespace gates this test
		g.By("checking for the cluster version operator")
		skipUnlessCVO(coreclient.CoreV1().Namespaces())

		// we need to get the list of versions
		cv, err := c.ConfigV1().ClusterVersions().Get(context.Background(), "version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		coList, err := c.ConfigV1().ClusterOperators().List(context.Background(), metav1.ListOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(coList.Items).NotTo(o.BeEmpty())

		g.By("all cluster operators report an operator version in the first position equal to the cluster version")
		for _, co := range coList.Items {
			msg := fmt.Sprintf("unexpected operator status versions %s:\n%#v", co.Name, co.Status.Versions)
			o.Expect(co.Status.Versions).NotTo(o.BeEmpty(), msg)
			operator := findOperatorVersion(co.Status.Versions, "operator")
			o.Expect(operator).NotTo(o.BeNil(), msg)
			o.Expect(operator.Name).To(o.Equal("operator"), msg)
			o.Expect(operator.Version).To(o.Equal(cv.Status.Desired.Version), msg)
		}
	})
})

func skipUnlessCVO(c coreclient.NamespaceInterface) {
	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
		_, err := c.Get(context.Background(), "openshift-cluster-version", metav1.GetOptions{})
		if err == nil {
			return true, nil
		}
		if errors.IsNotFound(err) {
			e2eskipper.Skipf("The cluster is not managed by a cluster-version operator")
		}
		e2e.Logf("Unable to check for cluster version operator: %v", err)
		return false, nil
	})
	o.Expect(err).NotTo(o.HaveOccurred())
}

func findOperatorVersion(versions []configv1.OperandVersion, name string) *configv1.OperandVersion {
	for i := range versions {
		if versions[i].Name == name {
			return &versions[i]
		}
	}
	return nil
}

func contains(names []string, name string) bool {
	for _, s := range names {
		if s == name {
			return true
		}
	}
	return false
}

func jsonString(from objx.Map) string {
	s, _ := from.JSON()
	return s
}

func objects(from *objx.Value) []objx.Map {
	var values []objx.Map
	switch {
	case from.IsObjxMapSlice():
		return from.ObjxMapSlice()
	case from.IsInterSlice():
		for _, i := range from.InterSlice() {
			if msi, ok := i.(map[string]interface{}); ok {
				values = append(values, objx.Map(msi))
			}
		}
	}
	return values
}

func condition(cv objx.Map, condition string) objx.Map {
	for _, obj := range objects(cv.Get("status.conditions")) {
		if obj.Get("type").String() == condition {
			return obj
		}
	}
	return objx.Map(nil)
}

// surprisingConditions returns conditions with surprising statuses
// (Available=False, Degraded=True, etc.) in order of descending
// severity (e.g. Available=False is more severe than Degraded=True).
// It also returns a slice of types for which a condition entry was
// expected but not supplied on the ClusterOperator.
func surprisingConditions(co objx.Map) ([]configv1.ClusterOperatorStatusCondition, []configv1.ClusterStatusConditionType) {
	var badConditions []configv1.ClusterOperatorStatusCondition
	var missingTypes []configv1.ClusterStatusConditionType
	for _, conditionType := range []configv1.ClusterStatusConditionType{
		configv1.OperatorAvailable,
		configv1.OperatorDegraded,
		configv1.OperatorProgressing,
	} {
		cond := condition(co, string(conditionType))
		if len(cond) == 0 {
			missingTypes = append(missingTypes, conditionType)
		} else {
			expected := configv1.ConditionFalse
			if conditionType == configv1.OperatorAvailable {
				expected = configv1.ConditionTrue
			}
			if cond.Get("status").String() != string(expected) {
				badConditions = append(badConditions, configv1.ClusterOperatorStatusCondition{
					Type:    conditionType,
					Status:  configv1.ConditionStatus(cond.Get("status").String()),
					Reason:  cond.Get("reason").String(),
					Message: cond.Get("message").String(),
				})
			}
		}
	}
	return badConditions, missingTypes
}
<|endoftext|>


<commit_before>package types

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// DateTime хранит дату-время и шаблон для преобразования при сериализации
type DateTime struct {
	time.Time
	Layout string
}

// Date хранит дату и шаблон для преобразования при сериализации
type Date struct {
	time.Time
	Layout string
}

// Шаблоны для сериализации
const (
	DateTimeLayout        = "2006-01-02 15:04:05"
	DateLayout            = "2006-01-02"
	GraphsDateLayout      = "02.01.2006"
	GraphsDateShortLayout = "02.01"
)

var defaultLocation *time.Location

// Задаёт часовой пояс по умолчанию
func init() {
	var err error
	defaultLocation, err = time.LoadLocation("Europe/Moscow")
	if err != nil {
		fmt.Println("Ошибка time.LoadLocation")
	}
}

// ToDateTime формирует объект типа DateTime на основе времени t и шаблона DateTimeLayout
func ToDateTime(t time.Time) DateTime {
	return DateTime{t.In(defaultLocation), DateTimeLayout}
}

// ToDate возвращает время типом Date по стандартному шаблону
func ToDate(t time.Time) Date {
	dS := fmt.Sprintf("%s %02d:%02d:%02d", t.Format(DateLayout), 0, 0, 0)
	d, _ := time.ParseInLocation(DateTimeLayout, dS, defaultLocation)
	return Date{d.In(defaultLocation), DateLayout}
}

// DaysBefore получает количество полных дней, прошедших от obj до b
// Если obj было вчера, b - сегодня, то возвращается 1
// Если b было раньше чем obj, то возвращается отрицательное число.
func (obj Date) DaysBefore(b Date) int {
	return int(b.Time.Sub(obj.Time).Hours() / 24)
}

// StringToDateTime преобразует строку по стандартному шаблону даты-времени в дату-время
func StringToDateTime(s string) (DateTime, error) {
	t, err := time.ParseInLocation(DateTimeLayout, s, defaultLocation)
	if err != nil {
		return DateTime{}, err
	}
	return ToDateTime(t), nil
}

// StringDateToDateTimeHMS преобразует строку по стандартному шаблону
// даты в дату-время с заданным значением часов, минут и секунд
func StringDateToDateTimeHMS(s string, hours int, mins int, secs int) (DateTime, error) {
	t, err := time.ParseInLocation(DateLayout, s, defaultLocation)
	if err != nil {
		return DateTime{}, err
	}
	d := ToDateTime(t)
	d = d.SetHMS(hours, mins, secs)
	return d, nil
}

// StringToDate преобразует строку по стандартному шаблону даты в дату
func StringToDate(s string) (Date, error) {
	t, err := time.ParseInLocation(DateLayout, s, defaultLocation)
	if err != nil {
		return Date{}, err
	}
	return ToDate(t), nil
}

// NeverDate возвращает дату в далёком прошлом
func NeverDate() Date {
	t, _ := time.ParseInLocation(DateLayout, "1990-01-01", defaultLocation)
	return ToDate(t)
}

// DateNow возвращает дату сегодня
func DateNow() Date {
	return ToDate(time.Now())
}

// DateTimeNow возвращает дату-время сейчас
func DateTimeNow() DateTime {
	return ToDateTime(time.Now())
}

// DateTimeTodayHMS возвращает дату-время сегодня в заданными значениями
// часов, минут, секунд
func DateTimeTodayHMS(hours int, mins int, secs int) DateTime {
	d := ToDateTime(time.Now())
	return d.SetHMS(hours, mins, secs)
}

// NeverTime возвращает дату-время в далёком прошлом
func NeverTime() DateTime {
	t, _ := time.ParseInLocation(DateTimeLayout, "1990-01-01 00:00:00", defaultLocation)
	return ToDateTime(t)
}

// setDefaultLayoutIfEmpty устанавливает шаблон вывода даты-времени
// по умолчанию, если шаблон не установлен
func (obj *DateTime) setDefaultLayoutIfEmpty() {
	if strings.TrimSpace(obj.Layout) == "" {
		obj.Layout = DateTimeLayout
	}
}

// setDefaultLayoutIfEmpty устанавливает шаблон вывода даты по умолчанию, если шаблон не установлен
func (obj *Date) setDefaultLayoutIfEmpty() {
	if strings.TrimSpace(obj.Layout) == "" {
		obj.Layout = DateLayout
	}
}

// SetHMS устанавливает значения часов, минут и секунд
func (obj DateTime) SetHMS(hours int, mins int, secs int) DateTime {
	t := obj.Time
	dS := fmt.Sprintf("%s %02d:%02d:%02d", t.Format(DateLayout), hours, mins, secs)
	d, _ := time.ParseInLocation(DateTimeLayout, dS, defaultLocation)
	obj.Time = d
	return obj
}

// ConvertToDate преобразует дату-время в дату
func (obj DateTime) ConvertToDate() Date {
	dS := obj.Time.Format(DateLayout)
	t, _ := time.ParseInLocation(DateLayout, dS, defaultLocation)
	return ToDate(t)
}

// ConvertToDateTimeHMS преобразует дату в дату-время с заданными значениями часов, минут и секунд
func (obj Date) ConvertToDateTimeHMS(hours int, mins int, secs int) DateTime {
	d := DateTime{
		Time:   obj.Time,
		Layout: obj.Layout,
	}
	d = d.SetHMS(hours, mins, secs)

	return d
}

// After возвращает true если дата obj позднее d, иначе false
// Сравнение с точностью до дня.
func (obj Date) After(d Date) bool {
	return obj.Time.After(d.Time)
}

// Before возвращает true если дата obj ранее d, иначе false
// Сравнение с точностью до дня.
func (obj Date) Before(d Date) bool {
	return obj.Time.Before(d.Time)
}

// Between возвращает true если дата obj находится в интервале дат (d1; d2), иначе false
// Сравнение с точностью до дня.
func (obj Date) Between(d1, d2 Date) bool {
	return obj.After(d1) && obj.Before(d2)
}

// After возвращает true если дата-время obj позднее d, иначе false
func (obj DateTime) After(d DateTime) bool {
	return obj.Time.After(d.Time)
}

// Before возвращает true если дата-время obj ранее d, иначе false
func (obj DateTime) Before(d DateTime) bool {
	return obj.Time.Before(d.Time)
}

// Between возвращает true если дата-время obj находится в интервале
// даты-времени (d1; d2), иначе false
func (obj DateTime) Between(d1, d2 DateTime) bool {
	return obj.After(d1) && obj.Before(d2)
}

// UnmarshalJSON - правило преобразования поля JSON в объект DateTime
func (obj *DateTime) UnmarshalJSON(data []byte) error {
	obj.setDefaultLayoutIfEmpty()
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	t, err := time.ParseInLocation(obj.Layout, s, defaultLocation)
	if err != nil {
		return err
	}
	obj.Time = t
	return nil
}

// MarshalJSON - правило преобразования объекта DateTime в поле JSON
func (obj DateTime) MarshalJSON() ([]byte, error) {
	obj.setDefaultLayoutIfEmpty()
	return []byte(strconv.Quote(obj.String())), nil
}

// UnmarshalJSON - правило преобразования поля JSON в объект Date
func (obj *Date) UnmarshalJSON(data []byte) error {
	obj.setDefaultLayoutIfEmpty()
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	t, err := time.ParseInLocation(obj.Layout, s, defaultLocation)
	if err != nil {
		return err
	}
	obj.Time = t
	return nil
}

// MarshalJSON - правило преобразования объекта Date в поле JSON
func (obj Date) MarshalJSON() ([]byte, error) {
	obj.setDefaultLayoutIfEmpty()
	return []byte(strconv.Quote(obj.String())), nil
}

// String преобразует объект DateTime в строку согласно заданного шаблона
func (obj DateTime) String() string {
	obj.setDefaultLayoutIfEmpty()
	return obj.Time.Format(obj.Layout)
}

// String преобразует объект Date в строку согласно заданного шаблона
func (obj Date) String() string {
	obj.setDefaultLayoutIfEmpty()
	return obj.Time.Format(obj.Layout)
}

func scanInternal(value interface{}) (time.Time, error) {
	t := time.Time{}
	if value == nil {
		return t, nil
	}
	t, ok := value.(time.Time)
	if !ok {
		return t, errors.New("Ошибка преобразования значения к типу time.Time")
	}
	return t.In(defaultLocation), nil
}

// Scan преобразует значение времени в БД к типу DateTime
func (obj *DateTime) Scan(value interface{}) error {
	obj.setDefaultLayoutIfEmpty()
	t, err := scanInternal(value)
	if err != nil {
		return err
	}
	obj.Time = t
	return nil
}

// Value преобразует значение типа DateTime к значению в БД
func (obj DateTime) Value() (driver.Value, error) {
	return obj.Time.In(defaultLocation).Format(DateTimeLayout), nil
}

// Scan преобразует значение времени в БД к типу Date
func (obj *Date) Scan(value interface{}) error {
	obj.setDefaultLayoutIfEmpty()
	t, err := scanInternal(value)
	if err != nil {
		return err
	}
	obj.Time = t
	return nil
}

// Value преобразует значение типа Date к значению в БД
func (obj Date) Value() (driver.Value, error) {
	return obj.Time.In(defaultLocation).Format(DateLayout), nil
}
<commit_msg>Убрал obj<commit_after>package types

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// DateTime хранит дату-время и шаблон для преобразования при сериализации
type DateTime struct {
	time.Time
	Layout string
}

// Date хранит дату и шаблон для преобразования при сериализации
type Date struct {
	time.Time
	Layout string
}

// Шаблоны для сериализации
const (
	DateTimeLayout        = "2006-01-02 15:04:05"
	DateLayout            = "2006-01-02"
	GraphsDateLayout      = "02.01.2006"
	GraphsDateShortLayout = "02.01"
)

var defaultLocation *time.Location

// Задаёт часовой пояс по умолчанию
func init() {
	var err error
	defaultLocation, err = time.LoadLocation("Europe/Moscow")
	if err != nil {
		fmt.Println("Ошибка time.LoadLocation")
	}
}

// ToDateTime формирует объект типа DateTime на основе времени t и шаблона DateTimeLayout
func ToDateTime(t time.Time) DateTime {
	return DateTime{t.In(defaultLocation), DateTimeLayout}
}

// ToDate формирует объект типа Date на основе времени t и шаблона DateLayout
func ToDate(t time.Time) Date {
	dS := fmt.Sprintf("%s %02d:%02d:%02d", t.Format(DateLayout), 0, 0, 0)
	d, _ := time.ParseInLocation(DateTimeLayout, dS, defaultLocation)
	return Date{d.In(defaultLocation), DateLayout}
}

// DaysBefore возвращает количество полных дней, прошедших от d до endDate
// Если d было вчера, endDate - сегодня, то возвращается 1
// Если endDate было раньше чем d, то возвращается отрицательное число.
func (d Date) DaysBefore(endDate Date) int {
	return int(endDate.Time.Sub(d.Time).Hours() / 24)
}

// StringToDateTime преобразует строку по стандартному шаблону даты-времени в дату-время
func StringToDateTime(s string) (DateTime, error) {
	t, err := time.ParseInLocation(DateTimeLayout, s, defaultLocation)
	if err != nil {
		return DateTime{}, err
	}
	return ToDateTime(t), nil
}

// StringDateToDateTimeHMS преобразует строку по стандартному шаблону
// даты в дату-время с заданным значением часов, минут и секунд
func StringDateToDateTimeHMS(s string, hours int, mins int, secs int) (DateTime, error) {
	t, err := time.ParseInLocation(DateLayout, s, defaultLocation)
	if err != nil {
		return DateTime{}, err
	}
	d := ToDateTime(t)
	d = d.SetHMS(hours, mins, secs)
	return d, nil
}

// StringToDate преобразует строку по стандартному шаблону даты в дату
func StringToDate(s string) (Date, error) {
	t, err := time.ParseInLocation(DateLayout, s, defaultLocation)
	if err != nil {
		return Date{}, err
	}
	return ToDate(t), nil
}

// NeverDate возвращает дату в далёком прошлом
func NeverDate() Date {
	t, _ := time.ParseInLocation(DateLayout, "1990-01-01", defaultLocation)
	return ToDate(t)
}

// DateNow возвращает дату сегодня
func DateNow() Date {
	return ToDate(time.Now())
}

// DateTimeNow возвращает дату-время сейчас
func DateTimeNow() DateTime {
	return ToDateTime(time.Now())
}

// DateTimeTodayHMS возвращает дату-время сегодня в заданными значениями
// часов, минут, секунд
func DateTimeTodayHMS(hours int, mins int, secs int) DateTime {
	d := ToDateTime(time.Now())
	return d.SetHMS(hours, mins, secs)
}

// NeverTime возвращает дату-время в далёком прошлом
func NeverTime() DateTime {
	t, _ := time.ParseInLocation(DateTimeLayout, "1990-01-01 00:00:00", defaultLocation)
	return ToDateTime(t)
}

// setDefaultLayoutIfEmpty устанавливает шаблон вывода даты-времени
// по умолчанию, если шаблон не установлен
func (d *DateTime) setDefaultLayoutIfEmpty() {
	if strings.TrimSpace(d.Layout) == "" {
		d.Layout = DateTimeLayout
	}
}

// setDefaultLayoutIfEmpty устанавливает шаблон вывода даты по умолчанию, если шаблон не установлен
func (d *Date) setDefaultLayoutIfEmpty() {
	if strings.TrimSpace(d.Layout) == "" {
		d.Layout = DateLayout
	}
}

// SetHMS устанавливает значения часов, минут и секунд
func (d DateTime) SetHMS(hours int, mins int, secs int) DateTime {
	t := d.Time
	dS := fmt.Sprintf("%s %02d:%02d:%02d", t.Format(DateLayout), hours, mins, secs)
	dt, _ := time.ParseInLocation(DateTimeLayout, dS, defaultLocation)
	d.Time = dt
	return d
}

// ConvertToDate преобразует дату-время в дату
func (d DateTime) ConvertToDate() Date {
	dS := d.Time.Format(DateLayout)
	t, _ := time.ParseInLocation(DateLayout, dS, defaultLocation)
	return ToDate(t)
}

// ConvertToDateTimeHMS преобразует дату в дату-время с заданными значениями часов, минут и секунд
func (d Date) ConvertToDateTimeHMS(hours int, mins int, secs int) DateTime {
	dt := DateTime{
		Time:   d.Time,
		Layout: d.Layout,
	}
	dt = dt.SetHMS(hours, mins, secs)

	return dt
}

// After возвращает true если дата d позднее d1, иначе false
// Сравнение с точностью до дня.
func (d Date) After(d1 Date) bool {
	return d.Time.After(d1.Time)
}

// Before возвращает true если дата d ранее d1, иначе false
// Сравнение с точностью до дня.
func (d Date) Before(d1 Date) bool {
	return d.Time.Before(d1.Time)
}

// Between возвращает true если дата d находится в интервале дат (d1; d2), иначе false
// Сравнение с точностью до дня.
func (d Date) Between(d1, d2 Date) bool {
	return d.After(d1) && d.Before(d2)
}

// After возвращает true если дата-время obj позднее d1, иначе false
func (d DateTime) After(d1 DateTime) bool {
	return d.Time.After(d1.Time)
}

// Before возвращает true если дата-время obj ранее d1, иначе false
func (d DateTime) Before(d1 DateTime) bool {
	return d.Time.Before(d1.Time)
}

// Between возвращает true если дата-время d находится в интервале
// даты-времени (d1; d2), иначе false
func (d DateTime) Between(d1, d2 DateTime) bool {
	return d.After(d1) && d.Before(d2)
}

// UnmarshalJSON - правило преобразования поля JSON в объект DateTime
func (d *DateTime) UnmarshalJSON(data []byte) error {
	d.setDefaultLayoutIfEmpty()
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	t, err := time.ParseInLocation(d.Layout, s, defaultLocation)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

// MarshalJSON - правило преобразования объекта DateTime в поле JSON
func (d DateTime) MarshalJSON() ([]byte, error) {
	d.setDefaultLayoutIfEmpty()
	return []byte(strconv.Quote(d.String())), nil
}

// UnmarshalJSON - правило преобразования поля JSON в объект Date
func (d *Date) UnmarshalJSON(data []byte) error {
	d.setDefaultLayoutIfEmpty()
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	t, err := time.ParseInLocation(d.Layout, s, defaultLocation)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

// MarshalJSON - правило преобразования объекта Date в поле JSON
func (d Date) MarshalJSON() ([]byte, error) {
	d.setDefaultLayoutIfEmpty()
	return []byte(strconv.Quote(d.String())), nil
}

// String преобразует объект DateTime в строку согласно заданного шаблона
func (d DateTime) String() string {
	d.setDefaultLayoutIfEmpty()
	return d.Time.Format(d.Layout)
}

// String преобразует объект Date в строку согласно заданного шаблона
func (d Date) String() string {
	d.setDefaultLayoutIfEmpty()
	return d.Time.Format(d.Layout)
}

func scanInternal(value interface{}) (time.Time, error) {
	t := time.Time{}
	if value == nil {
		return t, nil
	}
	t, ok := value.(time.Time)
	if !ok {
		return t, errors.New("Ошибка преобразования значения к типу time.Time")
	}
	return t.In(defaultLocation), nil
}

// Scan преобразует значение времени в БД к типу DateTime
func (d *DateTime) Scan(value interface{}) error {
	d.setDefaultLayoutIfEmpty()
	t, err := scanInternal(value)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

// Value преобразует значение типа DateTime к значению в БД
func (d DateTime) Value() (driver.Value, error) {
	return d.Time.In(defaultLocation).Format(DateTimeLayout), nil
}

// Scan преобразует значение времени в БД к типу Date
func (d *Date) Scan(value interface{}) error {
	d.setDefaultLayoutIfEmpty()
	t, err := scanInternal(value)
	if err != nil {
		return err
	}
	d.Time = t
	return nil
}

// Value преобразует значение типа Date к значению в БД
func (d Date) Value() (driver.Value, error) {
	return d.Time.In(defaultLocation).Format(DateLayout), nil
}
<|endoftext|>