<commit_before>package uniter_test

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/goyaml"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/worker"
	"launchpad.net/juju-core/worker/uniter"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	stdtesting "testing"
	"time"
)

func TestPackage(t *stdtesting.T) {
	coretesting.MgoTestPackage(t)
}

type UniterSuite struct {
	testing.JujuConnSuite
	coretesting.HTTPSuite
	dataDir  string
	oldLcAll string
}

var _ = Suite(&UniterSuite{})

func (s *UniterSuite) SetUpSuite(c *C) {
	s.JujuConnSuite.SetUpSuite(c)
	s.HTTPSuite.SetUpSuite(c)
	s.dataDir = c.MkDir()
	toolsDir := environs.AgentToolsDir(s.dataDir, "unit-u-0")
	err := os.MkdirAll(toolsDir, 0755)
	c.Assert(err, IsNil)
	cmd := exec.Command("go", "build", "launchpad.net/juju-core/cmd/jujuc")
	cmd.Dir = toolsDir
	out, err := cmd.CombinedOutput()
	c.Logf(string(out))
	c.Assert(err, IsNil)
	s.oldLcAll = os.Getenv("LC_ALL")
	os.Setenv("LC_ALL", "en_US")
}

func (s *UniterSuite) TearDownSuite(c *C) {
	os.Setenv("LC_ALL", s.oldLcAll)
}

func (s *UniterSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)
	s.HTTPSuite.SetUpTest(c)
}

func (s *UniterSuite) TearDownTest(c *C) {
	s.HTTPSuite.TearDownTest(c)
	s.JujuConnSuite.TearDownTest(c)
}

func (s *UniterSuite) Reset(c *C) {
	s.JujuConnSuite.Reset(c)
}

type uniterTest struct {
	summary string
	steps   []stepper
}

func ut(summary string, steps ...stepper) uniterTest {
	return uniterTest{summary, steps}
}

type stepper interface {
	step(c *C, ctx *context)
}

type context struct {
	id            int
	path          string
	dataDir       string
	st            *state.State
	charms        coretesting.ResponseMap
	hooks         []string
	svc           *state.Service
	unit          *state.Unit
	uniter        *uniter.Uniter
	relation      *state.Relation
	relationUnits map[string]*state.RelationUnit
}

func (ctx *context) run(c *C, steps []stepper) {
	defer func() {
		if ctx.uniter != nil {
			err := ctx.uniter.Stop()
			c.Assert(err, IsNil)
		}
	}()
	for i, s := range steps {
		c.Logf("step %d", i)
		step(c, ctx, s)
	}
}

var goodHook = `
#!/bin/bash
juju-log UniterSuite-%d %s $JUJU_REMOTE_UNIT
`[1:]

var badHook = `
#!/bin/bash
juju-log UniterSuite-%d fail-%s $JUJU_REMOTE_UNIT
exit 1
`[1:]

func (ctx *context) writeHook(c *C, path string, good bool) {
	hook := badHook
	if good {
		hook = goodHook
	}
	content := fmt.Sprintf(hook, ctx.id, filepath.Base(path))
	err := ioutil.WriteFile(path, []byte(content), 0755)
	c.Assert(err, IsNil)
}

func (ctx *context) matchLogHooks(c *C) (bool, bool) {
	hookPattern := fmt.Sprintf(`^.* JUJU u/0(| [a-z-]+:[0-9]+): UniterSuite-%d ([0-9a-z-/ ]+)$`, ctx.id)
	hookRegexp := regexp.MustCompile(hookPattern)
	var actual []string
	for _, line := range strings.Split(c.GetTestLog(), "\n") {
		if parts := hookRegexp.FindStringSubmatch(line); parts != nil {
			actual = append(actual, parts[2]+parts[1])
		}
	}
	c.Logf("actual: %#v", actual)
	if len(actual) < len(ctx.hooks) {
		return false, false
	}
	for i, e := range ctx.hooks {
		if actual[i] != e {
			return false, false
		}
	}<commit_msg>uniter: hook tests more careful<commit_after>package uniter_test

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/goyaml"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	coretesting "launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/worker"
	"launchpad.net/juju-core/worker/uniter"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	stdtesting "testing"
	"time"
)

func TestPackage(t *stdtesting.T) {
	coretesting.MgoTestPackage(t)
}

type UniterSuite struct {
	testing.JujuConnSuite
	coretesting.HTTPSuite
	dataDir  string
	oldLcAll string
}

var _ = Suite(&UniterSuite{})

func (s *UniterSuite) SetUpSuite(c *C) {
	s.JujuConnSuite.SetUpSuite(c)
	s.HTTPSuite.SetUpSuite(c)
	s.dataDir = c.MkDir()
	toolsDir := environs.AgentToolsDir(s.dataDir, "unit-u-0")
	err := os.MkdirAll(toolsDir, 0755)
	c.Assert(err, IsNil)
	cmd := exec.Command("go", "build", "launchpad.net/juju-core/cmd/jujuc")
	cmd.Dir = toolsDir
	out, err := cmd.CombinedOutput()
	c.Logf(string(out))
	c.Assert(err, IsNil)
	s.oldLcAll = os.Getenv("LC_ALL")
	os.Setenv("LC_ALL", "en_US")
}

func (s *UniterSuite) TearDownSuite(c *C) {
	os.Setenv("LC_ALL", s.oldLcAll)
}

func (s *UniterSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)
	s.HTTPSuite.SetUpTest(c)
}

func (s *UniterSuite) TearDownTest(c *C) {
	s.HTTPSuite.TearDownTest(c)
	s.JujuConnSuite.TearDownTest(c)
}

func (s *UniterSuite) Reset(c *C) {
	s.JujuConnSuite.Reset(c)
}

type uniterTest struct {
	summary string
	steps   []stepper
}

func ut(summary string, steps ...stepper) uniterTest {
	return uniterTest{summary, steps}
}

type stepper interface {
	step(c *C, ctx *context)
}

type context struct {
	id            int
	path          string
	dataDir       string
	st            *state.State
	charms        coretesting.ResponseMap
	hooks         []string
	svc           *state.Service
	unit          *state.Unit
	uniter        *uniter.Uniter
	relation      *state.Relation
	relationUnits map[string]*state.RelationUnit
}

func (ctx *context) run(c *C, steps []stepper) {
	defer func() {
		if ctx.uniter != nil {
			err := ctx.uniter.Stop()
			c.Assert(err, IsNil)
		}
	}()
	for i, s := range steps {
		c.Logf("step %d", i)
		step(c, ctx, s)
	}
}

var goodHook = `
#!/bin/bash
juju-log UniterSuite-%d %s $JUJU_REMOTE_UNIT
`[1:]

var badHook = `
#!/bin/bash
juju-log UniterSuite-%d fail-%s $JUJU_REMOTE_UNIT
exit 1
`[1:]

func (ctx *context) writeHook(c *C, path string, good bool) {
	hook := badHook
	if good {
		hook = goodHook
	}
	content := fmt.Sprintf(hook, ctx.id, filepath.Base(path))
	err := ioutil.WriteFile(path, []byte(content), 0755)
	c.Assert(err, IsNil)
}

func (ctx *context) matchLogHooks(c *C) (match bool, overshoot bool) {
	// hookPattern matches juju-log calls as generated by writeHook.
	hookPattern := fmt.Sprintf(`^.* JUJU `+
		`u/0(| [a-z-]+:[0-9]+)`+ // juju-log badge; group matches relation id
		`: UniterSuite-%d`+ // test badge; prevents cross-pollution
		` ([0-9a-z-/ ]+)$`, // foo-relation-joined bar/123
		ctx.id,
	)
	// donePattern matches uniter logging that indicates a hook has run.
	donePattern := `^.* JUJU (ran "[a-z-]+" hook|hook failed)`
	hookRegexp := regexp.MustCompile(hookPattern)
	doneRegexp := regexp.MustCompile(donePattern)

	// pending is empty while we scan for a new hook, and holds a value while
	// we scan for output indicating that hook execution has finished; at which
	// point, we add it to...
	pending := ""
	// ...actual, which holds a list of executed hooks, with basic information
	// about the relation context, and which will be compared against ctx's
	// complete list of expected hooks.
	var actual []string
	for _, line := range strings.Split(c.GetTestLog(), "\n") {
		if pending == "" {
			if parts := hookRegexp.FindStringSubmatch(line); parts != nil {
				// "hook-name[ JUJU_REMOTE_UNIT]" + "[ JUJU_RELATION_ID]
				pending = parts[2] + parts[1]
			}
		} else if doneRegexp.MatchString(line) {
			actual = append(actual, pending)
			pending = ""
		}
	}
	c.Logf("actual: %#v", actual)
	if len(actual) < len(ctx.hooks) {
		return false, false
	}
	for i, e := range ctx.hooks {
		if actual[i] != e {
			return false, false
		}
	}
{'fraction_non_alphanumeric': 0.15608941418293937, 'fraction_numerical': 0.006038026721479959, 'mean_word_length': 3.70392749244713, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4984517', 'n_tokens_mistral': 3323, 'n_tokens_neox': 3158, 'n_words': 668}