Merge branch 'master' into weekly

Conflicts:
	client/connection.go  # MERGED
	event/registry.go     # DELETED
	logging/mock_test.go  # DELETED
This commit is contained in:
Alex Bramley 2011-11-13 14:13:55 +00:00
commit e0a5a57fe1
15 changed files with 10 additions and 1038 deletions

View File

@ -5,11 +5,10 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"github.com/fluffle/goirc/event" "github.com/fluffle/goevent/event"
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
"github.com/fluffle/goirc/state" "github.com/fluffle/goirc/state"
"net" "net"
"strings" "strings"
"time" "time"
) )

View File

@ -2,8 +2,8 @@ package client
import ( import (
"bufio" "bufio"
"github.com/fluffle/goirc/event" "github.com/fluffle/goevent/event"
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
"github.com/fluffle/goirc/state" "github.com/fluffle/goirc/state"
"gomock.googlecode.com/hg/gomock" "gomock.googlecode.com/hg/gomock"
"testing" "testing"

View File

@ -4,7 +4,7 @@ package client
// to manage tracking an irc connection etc. // to manage tracking an irc connection etc.
import ( import (
"github.com/fluffle/goirc/event" "github.com/fluffle/goevent/event"
"strings" "strings"
) )

View File

@ -4,7 +4,7 @@ package client
// to manage tracking state for an IRC connection // to manage tracking state for an IRC connection
import ( import (
"github.com/fluffle/goirc/event" "github.com/fluffle/goevent/event"
"strings" "strings"
) )

View File

@ -1,127 +0,0 @@
// Automatically generated by MockGen. DO NOT EDIT!
// Source: registry.go
package event
import (
gomock "gomock.googlecode.com/hg/gomock"
)
// Mock of Handler interface
type MockHandler struct {
ctrl *gomock.Controller
recorder *_MockHandlerRecorder
}
// Recorder for MockHandler (not exported)
type _MockHandlerRecorder struct {
mock *MockHandler
}
func NewMockHandler(ctrl *gomock.Controller) *MockHandler {
mock := &MockHandler{ctrl: ctrl}
mock.recorder = &_MockHandlerRecorder{mock}
return mock
}
func (m *MockHandler) EXPECT() *_MockHandlerRecorder {
return m.recorder
}
func (m *MockHandler) Run(_param0 ...interface{}) {
m.ctrl.Call(m, "Run", _param0)
}
func (mr *_MockHandlerRecorder) Run(arg0 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Run", arg0)
}
func (m *MockHandler) Id() HandlerID {
ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(HandlerID)
return ret0
}
func (mr *_MockHandlerRecorder) Id() *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Id")
}
// Mock of EventDispatcher interface
type MockEventDispatcher struct {
ctrl *gomock.Controller
recorder *_MockEventDispatcherRecorder
}
// Recorder for MockEventDispatcher (not exported)
type _MockEventDispatcherRecorder struct {
mock *MockEventDispatcher
}
func NewMockEventDispatcher(ctrl *gomock.Controller) *MockEventDispatcher {
mock := &MockEventDispatcher{ctrl: ctrl}
mock.recorder = &_MockEventDispatcherRecorder{mock}
return mock
}
func (m *MockEventDispatcher) EXPECT() *_MockEventDispatcherRecorder {
return m.recorder
}
func (m *MockEventDispatcher) Dispatch(name string, ev ...interface{}) {
m.ctrl.Call(m, "Dispatch", name, ev)
}
func (mr *_MockEventDispatcherRecorder) Dispatch(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Dispatch", arg0, arg1)
}
// Mock of EventRegistry interface
type MockEventRegistry struct {
ctrl *gomock.Controller
recorder *_MockEventRegistryRecorder
}
// Recorder for MockEventRegistry (not exported)
type _MockEventRegistryRecorder struct {
mock *MockEventRegistry
}
func NewMockEventRegistry(ctrl *gomock.Controller) *MockEventRegistry {
mock := &MockEventRegistry{ctrl: ctrl}
mock.recorder = &_MockEventRegistryRecorder{mock}
return mock
}
func (m *MockEventRegistry) EXPECT() *_MockEventRegistryRecorder {
return m.recorder
}
func (m *MockEventRegistry) AddHandler(h Handler, names ...string) {
m.ctrl.Call(m, "AddHandler", h, names)
}
func (mr *_MockEventRegistryRecorder) AddHandler(arg0 interface{}, arg1 ...string) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "AddHandler", arg0, arg1)
}
func (m *MockEventRegistry) DelHandler(h Handler, names ...string) {
m.ctrl.Call(m, "DelHandler", h, names)
}
func (mr *_MockEventRegistryRecorder) DelHandler(arg0 interface{}, arg1 ...string) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "DelHandler", arg0, arg1)
}
func (m *MockEventRegistry) Dispatch(name string, ev ...interface{}) {
m.ctrl.Call(m, "Dispatch", name, ev)
}
func (mr *_MockEventRegistryRecorder) Dispatch(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Dispatch", arg0, arg1)
}
func (m *MockEventRegistry) ClearEvents(name string) {
m.ctrl.Call(m, "ClearEvents", name)
}
func (mr *_MockEventRegistryRecorder) ClearEvents(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "ClearEvents", arg0)
}

View File

@ -1,163 +0,0 @@
package event
import (
"container/list"
"strings"
"sync"
"sync/atomic"
)
type HandlerID uint32
var hidCounter uint32 = 0
func NewHandlerID() HandlerID {
return HandlerID(atomic.AddUint32(&hidCounter, 1))
}
type Handler interface {
Run(...interface{})
Id() HandlerID
}
type basicHandler struct {
fn func(...interface{})
id HandlerID
}
func (h *basicHandler) Run(ev ...interface{}) {
h.fn(ev...)
}
func (h *basicHandler) Id() HandlerID {
return h.id
}
func NewHandler(h func(...interface{})) Handler {
return &basicHandler{h, NewHandlerID()}
}
type EventDispatcher interface {
Dispatch(name string, ev ...interface{})
}
type EventRegistry interface {
AddHandler(h Handler, names ...string)
DelHandler(h Handler, names ...string)
Dispatch(name string, ev ...interface{})
ClearEvents(name string)
}
type registry struct {
// Event registry as a lockable map of linked-lists
sync.RWMutex
events map[string]*list.List
dispatcher func(r *registry, name string, ev ...interface{})
}
func NewRegistry() *registry {
r := &registry{events: make(map[string]*list.List)}
r.Parallel()
return r
}
func (r *registry) AddHandler(h Handler, names ...string) {
if len(names) == 0 {
return
}
r.Lock()
defer r.Unlock()
N:
for _, name := range names {
name = strings.ToLower(name)
if _, ok := r.events[name]; !ok {
r.events[name] = list.New()
}
for e := r.events[name].Front(); e != nil; e = e.Next() {
// Check we're not adding a duplicate handler to this event
if e.Value.(Handler).Id() == h.Id() {
continue N
}
}
r.events[name].PushBack(h)
}
}
func _del(l *list.List, id HandlerID) bool {
for e := l.Front(); e != nil; e = e.Next() {
if e.Value.(Handler).Id() == id {
l.Remove(e)
}
}
return l.Len() == 0
}
func (r *registry) DelHandler(h Handler, names ...string) {
r.Lock()
defer r.Unlock()
if len(names) == 0 {
for name, l := range r.events {
if _del(l, h.Id()) {
delete(r.events, name)
}
}
} else {
for _, name := range names {
name = strings.ToLower(name)
if l, ok := r.events[name]; ok {
if _del(l, h.Id()) {
delete(r.events, name)
}
}
}
}
}
func (r *registry) Dispatch(name string, ev ...interface{}) {
r.dispatcher(r, strings.ToLower(name), ev...)
}
func (r *registry) ClearEvents(name string) {
name = strings.ToLower(name)
r.Lock()
defer r.Unlock()
if l, ok := r.events[name]; ok {
l.Init() // I hope this is enough to GC all list elements.
delete(r.events, name)
}
}
func (r *registry) Parallel() {
r.dispatcher = (*registry).parallelDispatch
}
func (r *registry) Serial() {
r.dispatcher = (*registry).serialDispatch
}
func (r *registry) parallelDispatch(name string, ev ...interface{}) {
r.RLock()
defer r.RUnlock()
if l, ok := r.events[name]; ok {
for e := l.Front(); e != nil; e = e.Next() {
h := e.Value.(Handler)
go h.Run(ev...)
}
}
}
func (r *registry) serialDispatch(name string, ev ...interface{}) {
r.RLock()
defer r.RUnlock()
if l, ok := r.events[name]; ok {
hlist := make([]Handler, l.Len())
for e, i := l.Front(), 0; e != nil; e, i = e.Next(), i+1 {
hlist[i] = e.Value.(Handler)
}
go func() {
for _, h := range hlist {
h.Run(ev...)
}
}()
}
}

View File

@ -1,224 +0,0 @@
package event
// oh hey unit tests. or functionality tests, or something.
import (
"testing"
"time"
)
func TestAddDelHandler(t *testing.T) {
r := NewRegistry()
if len(r.events) != 0 {
t.Errorf("New registry has non-zero-length event map.")
}
h1 := NewHandler(func(ev ...interface{}) {})
h2 := NewHandler(func(ev ...interface{}) {})
// Ensure that a handler with no events to handle doesn't get added
r.AddHandler(h1)
if len(r.events) != 0 {
t.Errorf("Adding handler with no events succeded.")
}
// Add h1 to a couple of events.
r.AddHandler(h1, "e1", "E2")
if len(r.events) != 2 {
t.Errorf("Adding handler h1 to events failed.")
}
if l, ok := r.events["e1"]; !ok || l.Len() != 1 ||
l.Front().Value != h1 {
t.Errorf("Handler h1 not added to event e1 correctly.")
}
if l, ok := r.events["e2"]; !ok || l.Len() != 1 ||
l.Front().Value != h1 {
t.Errorf("Handler h1 not added to event e2 correctly.")
}
// Add h2 to a couple of events.
r.AddHandler(h2, "e2", "E3")
if len(r.events) != 3 {
t.Errorf("Adding handler h2 to events failed.")
}
if l, ok := r.events["e2"]; !ok || l.Len() != 2 ||
l.Front().Next().Value.(Handler) != h2 {
t.Errorf("Handler h2 not added to event e2 correctly.")
}
if l, ok := r.events["e3"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h2 {
t.Errorf("Handler h2 not added to event e3 correctly.")
}
// Add h1 to some more events, which it may be in already.
r.AddHandler(h1, "e2", "e3", "e4", "e5")
if len(r.events) != 5 {
t.Errorf("Adding handler h1 to more events failed.")
println(len(r.events))
}
if l, ok := r.events["e2"]; !ok || l.Len() != 2 {
t.Errorf("Handler h1 added twice to event e2.")
}
if l, ok := r.events["e3"]; !ok || l.Len() != 2 ||
l.Front().Next().Value.(Handler) != h1 {
t.Errorf("Handler h1 not added to event e3 correctly.")
}
// Add h2 to a few more events, for testing delete
r.AddHandler(h2, "e5", "e6")
if len(r.events) != 6 {
t.Errorf("Adding handler h2 to more events failed.")
}
// Currently, we have the following handlers set up:
// h1: e1 e2 e3 e4 e5
// h2: e2 e3 e5 e6
// NOTE: for e3, h2 is first and h1 is second in the linked list
// Delete h1 from a few events. This should remove e4 completely.
r.DelHandler(h1, "e2", "E3", "e4")
if _, ok := r.events["e4"]; ok || len(r.events) != 5 {
t.Errorf("Deleting h1 from some events failed to remove e4.")
}
if l, ok := r.events["e2"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h2 {
t.Errorf("Handler h1 not deleted from event e2 correctly.")
}
if l, ok := r.events["e3"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h2 {
t.Errorf("Handler h1 not deleted from event e3 correctly.")
}
// Now, we have the following handlers set up:
// h1: e1 e5
// h2: e2 e3 e5 e6
// Delete h2 from a couple of events, removing e2 and e3.
// Deleting h2 from a handler it is not in should not cause problems.
r.DelHandler(h2, "e1", "e2", "e3")
if len(r.events) != 3 {
t.Errorf("Deleting h2 from some events failed to remove e{2,3}.")
}
if l, ok := r.events["e1"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h1 {
t.Errorf("Handler h1 deleted from event e1 incorrectly.")
}
// Delete h1 completely.
r.DelHandler(h1)
if _, ok := r.events["e1"]; ok || len(r.events) != 2 {
t.Errorf("Deleting h1 completely failed to remove e1.")
}
if l, ok := r.events["e5"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h2 {
t.Errorf("Handler h1 deleted from event e5 incorrectly.")
}
// Clear e5 completely
r.ClearEvents("e5")
if _, ok := r.events["e5"]; ok || len(r.events) != 1 {
t.Errorf("Deleting e5 completely failed to remove it.")
}
// All that should be left is e6, with h2 as it's only handler.
if l, ok := r.events["e6"]; !ok || l.Len() != 1 ||
l.Front().Value.(Handler) != h2 {
t.Errorf("Remaining event and handler doesn't match expectations.")
}
}
func TestSimpleDispatch(t *testing.T) {
r := NewRegistry()
out := make(chan bool)
h := NewHandler(func(ev ...interface{}) {
out <- ev[0].(bool)
})
r.AddHandler(h, "send")
r.Dispatch("send", true)
if val := <-out; !val {
t.Fail()
}
r.Dispatch("send", false)
if val := <-out; val {
t.Fail()
}
}
func TestParallelDispatch(t *testing.T) {
r := NewRegistry()
// ensure we have enough of a buffer that all sends complete
out := make(chan int, 5)
// handler factory :-)
factory := func(t int) Handler {
return NewHandler(func(ev ...interface{}) {
// t * 10ms sleep
time.Sleep(int64(t * 1e7))
out <- t
})
}
// create some handlers and send an event to them
for _, t := range []int{5, 11, 2, 15, 8} {
r.AddHandler(factory(t), "send")
}
r.Dispatch("send")
// If parallel dispatch is working, results from out should be in numerical order
if val := <-out; val != 2 {
t.Fail()
}
if val := <-out; val != 5 {
t.Fail()
}
if val := <-out; val != 8 {
t.Fail()
}
if val := <-out; val != 11 {
t.Fail()
}
if val := <-out; val != 15 {
t.Fail()
}
}
func TestSerialDispatch(t *testing.T) {
r := NewRegistry()
r.Serial()
// ensure we have enough of a buffer that all sends complete
out := make(chan int, 5)
// handler factory :-)
factory := func(t int) Handler {
return NewHandler(func(ev ...interface{}) {
// t * 10ms sleep
time.Sleep(int64(t * 1e7))
out <- t
})
}
// create some handlers and send an event to them
for _, t := range []int{5, 11, 2, 15, 8} {
r.AddHandler(factory(t), "send")
}
r.Dispatch("send")
// If serial dispatch is working, results from out should be in handler order
if val := <-out; val != 5 {
t.Fail()
}
if val := <-out; val != 11 {
t.Fail()
}
if val := <-out; val != 2 {
t.Fail()
}
if val := <-out; val != 15 {
t.Fail()
}
if val := <-out; val != 8 {
t.Fail()
}
}

View File

@ -1,235 +0,0 @@
package logging
import (
"flag"
"fmt"
"io"
"log"
"os"
"sync"
)
// A simple level-based logging system.
// Note that higher levels of logging are still usable via Log(). They will be
// output to the debug log in split mode if --log.level is set high enough.
// Also, remember to call flag.Parse() near the start of your func main()!
// The enforced singleton style of the standard "log" pkg is very nice, but
// it encourages people to write less testable code, and while logging is one
// of the few places where a singleton is not necessarily bad practise, it's
// not *that* hard to propagate your logging to where it needs to be.
// Alternatively you can create your own damn singleton with this package ;-)
type LogLevel int
type LogMap map[LogLevel]*log.Logger
const (
Fatal LogLevel = iota - 1
Error
Warn
Info
Debug
)
var logString map[LogLevel]string = map[LogLevel]string{
Fatal: "FATAL",
Error: "ERROR",
Warn: "WARN",
Info: "INFO",
Debug: "DEBUG",
}
func LogString(lv LogLevel) string {
if s, ok := logString[lv]; ok {
return s
}
return fmt.Sprintf("LOG(%d)", lv)
}
var (
file = flag.String("log.file", "",
"Log to this file rather than STDERR")
level = flag.Int("log.level", int(Error),
"Level of logging to be output")
only = flag.Bool("log.only", false,
"Only log output at the selected level")
split = flag.Bool("log.split", false,
"Log to one file per log level Error/Warn/Info/Debug.")
// Shortcut flags for great justice
quiet = flag.Bool("log.quiet", false,
"Only fatal output (equivalent to -v -1)")
warn = flag.Bool("log.warn", false,
"Warning output (equivalent to -v 1)")
info = flag.Bool("log.info", false,
"Info output (equivalent to -v 2)")
debug = flag.Bool("log.debug", false,
"Debug output (equivalent to -v 3)")
)
type Logger interface {
// Log at a given level
Log(LogLevel, string, ...interface{})
// Log at level 3
Debug(string, ...interface{})
// Log at level 2
Info(string, ...interface{})
// Log at level 1
Warn(string, ...interface{})
// Log at level 0
Error(string, ...interface{})
// Log at level -1, to STDERR always, and exit after logging.
Fatal(string, ...interface{})
// Change the current log display level
SetLogLevel(LogLevel)
// Set the logger to only output the current level
SetOnly(bool)
}
// A struct to implement the above interface
type logger struct {
// We wrap a set of log.Logger for most of the heavy lifting
// but it can't be anonymous thanks to the conflicting definitions of Fatal
log LogMap
level LogLevel
only bool
*sync.Mutex // to ensure changing levels/flags is atomic
}
// Helper function for opening log files, causes lots of Fatal :-)
func openLog(fn string) *log.Logger {
fh, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatalf("Error opening log file: %s", err)
}
return makeLogger(fh)
}
// Helper function to create log.Loggers out of io.Writers
func makeLogger(w io.Writer) *log.Logger {
return log.New(w, "", log.LstdFlags | log.Lshortfile)
}
// Creates a new logger object using the flags declared above.
// You MUST call flag.Parse before calling this ;-)
// Calling this more than once is inadvisable, you may get log corruption.
func NewFromFlags() *logger {
// Sanity checks: if log.split is set, must have a log.file.
if *split && *file == "" {
log.Fatalf("You must pass --log.file with --log.split")
}
lv := Error
logMap := make(LogMap)
// What are we logging?
// The shortcut flags prioritize by level, but an
// explicit level flag takes first precedence.
// I think the switch looks cleaner than if/else if, meh :-)
switch {
case *level != 0:
lv = LogLevel(*level)
case *quiet:
lv = Fatal
case *warn:
lv = Warn
case *info:
lv = Info
case *debug:
lv = Debug
}
// Where are we logging to?
if *split {
// Fill in the logger map.
for l := Fatal; l <= Debug; l++ {
logMap[l] = openLog(*file + "." + logString[l])
}
} else {
var _log *log.Logger
if *file != "" {
_log = openLog(*file)
} else {
_log = makeLogger(os.Stderr)
}
for l := Fatal; l <= Debug; l++ {
logMap[l] = _log
}
}
return New(logMap, lv, *only)
}
// You'll have to set up your own loggers for this one...
func New(m LogMap, lv LogLevel, only bool) *logger {
// Sanity check the log map we've been passed.
// We need loggers for all levels in case SetLogLevel is called.
for l := Fatal; l <= Debug; l++ {
if _log, ok := m[l]; !ok || _log == nil {
log.Fatalf("Output log level %s has no logger configured.",
logString[l])
}
}
return &logger{m, lv, only, &sync.Mutex{}}
}
// Internal function all others call to ensure identical call depth
func (l *logger) write(lv LogLevel, fm string, v ...interface{}) {
if lv > l.level || (l.only && lv != l.level) {
// Your logs are not important to us, goodnight
return
}
fm = fmt.Sprintf(LogString(lv)+" "+fm, v...)
if lv > Debug || lv < Fatal {
// This is an unrecognised log level, so log it to Debug
lv = Debug
}
l.Lock()
defer l.Unlock()
// Writing the log is deceptively simple
l.log[lv].Output(3, fm)
if lv == Fatal {
// Always fatal to stderr too. Use panic so (a) we get a backtrace,
// and (b) it's trappable for testing (and maybe other times too).
log.Panic(fm)
}
}
func (l *logger) Log(lv LogLevel, fm string, v ...interface{}) {
l.write(lv, fm, v...)
}
// Helper functions for specific levels
func (l *logger) Debug(fm string, v ...interface{}) {
l.write(Debug, fm, v...)
}
func (l *logger) Info(fm string, v ...interface{}) {
l.write(Info, fm, v...)
}
func (l *logger) Warn(fm string, v ...interface{}) {
l.write(Warn, fm, v...)
}
func (l *logger) Error(fm string, v ...interface{}) {
l.write(Error, fm, v...)
}
func (l *logger) Fatal(fm string, v ...interface{}) {
l.write(Fatal, fm, v...)
}
func (l *logger) SetLogLevel(lv LogLevel) {
l.Lock()
defer l.Unlock()
l.level = lv
}
func (l *logger) SetOnly(only bool) {
l.Lock()
defer l.Unlock()
l.only = only
}

View File

@ -1,60 +0,0 @@
package logging
import (
"testing"
)
// Note: the below is deliberately PLACED AT THE TOP OF THIS FILE because
// it is fragile. It ensures the right file:line is logged. Sorry!
func TestLogCorrectLineNumbers(t *testing.T) {
l, m := newMock(t)
l.Log(Error, "Error!")
// This breaks the mock encapsulation a little, but meh.
if s := string(m.m[Error].written); s[20:] != "logging_test.go:11: ERROR Error!\n" {
t.Errorf("Error incorrectly logged (check line numbers!)")
}
}
func TestStandardLogging(t *testing.T) {
l, m := newMock(t)
l.SetLogLevel(Error)
l.Log(4, "Nothing should be logged yet")
m.ExpectNothing()
l.Log(Debug, "or yet...")
m.ExpectNothing()
l.Log(Info, "or yet...")
m.ExpectNothing()
l.Log(Warn, "or yet!")
m.ExpectNothing()
l.Log(Error, "Error!")
m.Expect("Error!")
}
func TestAllLoggingLevels(t *testing.T) {
l, m := newMock(t)
l.Log(4, "Log to level 4.")
m.ExpectAt(4, "Log to level 4.")
l.Debug("Log to debug.")
m.ExpectAt(Debug, "Log to debug.")
l.Info("Log to info.")
m.ExpectAt(Info, "Log to info.")
l.Warn("Log to warning.")
m.ExpectAt(Warn, "Log to warning.")
l.Error("Log to error.")
m.ExpectAt(Error, "Log to error.")
// recover to track the panic caused by Fatal.
defer func() { recover() }()
l.Fatal("Log to fatal.")
m.ExpectAt(Fatal, "Log to fatal.")
}

View File

@ -1,93 +0,0 @@
// Automatically generated by MockGen. DO NOT EDIT!
// Source: logging.go
package logging
import (
gomock "gomock.googlecode.com/hg/gomock"
)
// Mock of Logger interface
type MockLogger struct {
ctrl *gomock.Controller
recorder *_MockLoggerRecorder
}
// Recorder for MockLogger (not exported)
type _MockLoggerRecorder struct {
mock *MockLogger
}
func NewMockLogger(ctrl *gomock.Controller) *MockLogger {
mock := &MockLogger{ctrl: ctrl}
mock.recorder = &_MockLoggerRecorder{mock}
return mock
}
func (m *MockLogger) EXPECT() *_MockLoggerRecorder {
return m.recorder
}
func (m *MockLogger) Log(_param0 LogLevel, _param1 string, _param2 ...interface{}) {
m.ctrl.Call(m, "Log", _param0, _param1, _param2)
}
func (mr *_MockLoggerRecorder) Log(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Log", arg0, arg1, arg2)
}
func (m *MockLogger) Debug(_param0 string, _param1 ...interface{}) {
m.ctrl.Call(m, "Debug", _param0, _param1)
}
func (mr *_MockLoggerRecorder) Debug(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Debug", arg0, arg1)
}
func (m *MockLogger) Info(_param0 string, _param1 ...interface{}) {
m.ctrl.Call(m, "Info", _param0, _param1)
}
func (mr *_MockLoggerRecorder) Info(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Info", arg0, arg1)
}
func (m *MockLogger) Warn(_param0 string, _param1 ...interface{}) {
m.ctrl.Call(m, "Warn", _param0, _param1)
}
func (mr *_MockLoggerRecorder) Warn(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Warn", arg0, arg1)
}
func (m *MockLogger) Error(_param0 string, _param1 ...interface{}) {
m.ctrl.Call(m, "Error", _param0, _param1)
}
func (mr *_MockLoggerRecorder) Error(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Error", arg0, arg1)
}
func (m *MockLogger) Fatal(_param0 string, _param1 ...interface{}) {
m.ctrl.Call(m, "Fatal", _param0, _param1)
}
func (mr *_MockLoggerRecorder) Fatal(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "Fatal", arg0, arg1)
}
func (m *MockLogger) SetLogLevel(_param0 LogLevel) {
m.ctrl.Call(m, "SetLogLevel", _param0)
}
func (mr *_MockLoggerRecorder) SetLogLevel(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "SetLogLevel", arg0)
}
func (m *MockLogger) SetOnly(_param0 bool) {
m.ctrl.Call(m, "SetOnly", _param0)
}
func (mr *_MockLoggerRecorder) SetOnly(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCall(mr.mock, "SetOnly", arg0)
}

View File

@ -1,125 +0,0 @@
package logging
import (
"strings"
"testing"
)
// TODO(fluffle): Assumes at most one logging line will be written
// between calls to Expect*. Change to be Expect(exp []string)?
type mockWriter struct {
written []byte
}
func (w *mockWriter) Write(p []byte) (n int, err error) {
w.written = append(w.written, p...)
return len(p), nil
}
func (w *mockWriter) getLine() string {
// 20 bytes covers the date and time in
// 2011/10/22 10:22:57 <file>:<line>: <level> <log message>
if len(w.written) < 20 {
return ""
}
s := string(w.written)
idx := strings.Index(s, "\n")
s = s[20:idx]
w.written = w.written[idx+1:]
// consume '<file>:<line>: '
idx = strings.Index(s, ":") + 1
idx += strings.Index(s[idx:], ":") + 2
return s[idx:]
}
func (w *mockWriter) reset() {
w.written = w.written[:0]
}
type writerMap struct {
t *testing.T
m map[LogLevel]*mockWriter
}
// This doesn't create a mock Logger but a Logger that writes to mock outputs
// for testing purposes. Use the gomock-generated mock_logging package for
// external testing code that needs to mock out a logger.
func newMock(t *testing.T) (*logger, *writerMap) {
wMap := &writerMap{
t: t,
m: map[LogLevel]*mockWriter{
Debug: &mockWriter{make([]byte, 0)},
Info: &mockWriter{make([]byte, 0)},
Warn: &mockWriter{make([]byte, 0)},
Error: &mockWriter{make([]byte, 0)},
Fatal: &mockWriter{make([]byte, 0)},
},
}
logMap := make(LogMap)
for lv, w := range wMap.m {
logMap[lv] = makeLogger(w)
}
// Set the default log level high enough that everything will get logged
return New(logMap, (1<<31)-1, false), wMap
}
// When you expect something to be logged but don't care so much what level at.
func (wm *writerMap) Expect(exp string) {
found := false
for lv, w := range wm.m {
if s := w.getLine(); s != "" && !found {
// Since we don't know what log level we're expecting, compare
// exp against the log line with the level stripped.
idx := strings.Index(s, " ") + 1
if s[idx:] == exp {
found = true
} else {
wm.t.Errorf("Unexpected log message encountered at level %s:",
LogString(lv))
wm.t.Errorf("exp: %s\ngot: %s", exp, s[idx:])
}
}
}
wm.ExpectNothing()
if !found {
wm.t.Errorf("Expected log message not encountered:")
wm.t.Errorf("exp: %s", exp)
}
}
// When you expect nothing to be logged
func (wm *writerMap) ExpectNothing() {
for lv, w := range wm.m {
if s := w.getLine(); s != "" {
wm.t.Errorf("Unexpected log message at level %s:",
LogString(lv))
wm.t.Errorf("%s", s)
w.reset()
}
}
}
// When you expect something to be logged at a specific level.
func (wm *writerMap) ExpectAt(lv LogLevel, exp string) {
var w *mockWriter
if _, ok := wm.m[lv]; !ok {
w = wm.m[Debug]
} else {
w = wm.m[lv]
}
s := w.getLine()
exp = strings.Join([]string{LogString(lv), exp}, " ")
if s == "" {
wm.t.Errorf("Nothing logged at level %s:", LogString(lv))
wm.t.Errorf("exp: %s", exp)
// Check nothing was written to a different log level here, too.
wm.ExpectNothing()
return
}
if s != exp {
wm.t.Errorf("Log message at level %s differed.", LogString(lv))
wm.t.Errorf("exp: %s\ngot: %s", exp, s)
}
wm.ExpectNothing()
}

View File

@ -2,7 +2,7 @@ package state
import ( import (
"fmt" "fmt"
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
"reflect" "reflect"
"strconv" "strconv"
) )

View File

@ -1,7 +1,7 @@
package state package state
import ( import (
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
"reflect" "reflect"
) )

View File

@ -1,7 +1,7 @@
package state package state
import ( import (
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
) )
// The state manager interface // The state manager interface

View File

@ -1,7 +1,7 @@
package state package state
import ( import (
"github.com/fluffle/goirc/logging" "github.com/fluffle/golog/logging"
"gomock.googlecode.com/hg/gomock" "gomock.googlecode.com/hg/gomock"
"testing" "testing"
) )