Update to latest gomatrix. Things are broken

This commit is contained in:
Tulir Asokan 2018-09-05 10:55:48 +03:00
parent 68db26bcac
commit cfb2cc057c
56 changed files with 2467 additions and 936 deletions

40
Gopkg.lock generated
View File

@ -4,14 +4,14 @@
[[projects]] [[projects]]
name = "github.com/davecgh/go-spew" name = "github.com/davecgh/go-spew"
packages = ["spew"] packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.0" version = "v1.1.1"
[[projects]] [[projects]]
name = "github.com/disintegration/imaging" name = "github.com/disintegration/imaging"
packages = ["."] packages = ["."]
revision = "bbcee2f5c9d5e94ca42c8b50ec847fec64a6c134" revision = "0bd5694c78c9c3d9a3cd06a706a8f3c59296a9ac"
version = "v1.4.2" version = "v1.5.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -26,16 +26,16 @@
version = "v1.5.1" version = "v1.5.1"
[[projects]] [[projects]]
branch = "master"
name = "github.com/lucasb-eyer/go-colorful" name = "github.com/lucasb-eyer/go-colorful"
packages = ["."] packages = ["."]
revision = "8abd3beca3a7f5039809449d6013a0254ac22bb1" revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82"
version = "v1.0"
[[projects]] [[projects]]
name = "github.com/mattn/go-runewidth" name = "github.com/mattn/go-runewidth"
packages = ["."] packages = ["."]
revision = "9e777a8366cce605130a531d2cd6363d07ad7317" revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb"
version = "v0.0.2" version = "v0.0.3"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -50,10 +50,10 @@
version = "v1.0.0" version = "v1.0.0"
[[projects]] [[projects]]
branch = "master"
name = "github.com/renstrom/fuzzysearch" name = "github.com/renstrom/fuzzysearch"
packages = ["fuzzy"] packages = ["fuzzy"]
revision = "500e0fce37a81072d9bf4ec1bf5d32f52c807282" revision = "b18e754edff4833912ef4dce9eaca885bd3f0de1"
version = "v1.0.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -64,14 +64,14 @@
[[projects]] [[projects]]
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
packages = ["assert"] packages = ["assert"]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.1" version = "v1.2.2"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/zyedidia/clipboard" name = "github.com/zyedidia/clipboard"
packages = ["."] packages = ["."]
revision = "4611e809d8b1a3051c11d11f4b610c44df73fa38" revision = "bd31d747117d04b4e25b61f73e1ea4faeea3c56a"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -85,7 +85,7 @@
"vp8l", "vp8l",
"webp" "webp"
] ]
revision = "f315e440302883054d0c2bd85486878cb4f8572c" revision = "c73c2afc3b812cdd6385de5a50616511c4a3d458"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -94,7 +94,7 @@
"html", "html",
"html/atom" "html/atom"
] ]
revision = "dfa909b99c79129e1100513e5cd36307665e5723" revision = "8a410e7b638dca158bf9e766925842f6651ff828"
[[projects]] [[projects]]
name = "golang.org/x/text" name = "golang.org/x/text"
@ -118,7 +118,7 @@
branch = "v1" branch = "v1"
name = "gopkg.in/toast.v1" name = "gopkg.in/toast.v1"
packages = ["."] packages = ["."]
revision = "b700e246b8b6d3e13554091e540e1019e26389f1" revision = "0a84660828b24d25b35525c9a1f1f51267f8da91"
[[projects]] [[projects]]
name = "gopkg.in/yaml.v2" name = "gopkg.in/yaml.v2"
@ -130,7 +130,13 @@
branch = "master" branch = "master"
name = "maunium.net/go/gomatrix" name = "maunium.net/go/gomatrix"
packages = ["."] packages = ["."]
revision = "b491397f18b90e34ef54b8b3598666564b90b01a" revision = "d651abc3ecb4bbd85a6b17b710632e73ddbbc6aa"
[[projects]]
name = "maunium.net/go/maulogger"
packages = ["."]
revision = "64f0aa33b6c51313e15575257db71dec44fe7988"
version = "v1.0"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@ -33,14 +33,14 @@ type MatrixContainer interface {
Logout() Logout()
SendPreferencesToMatrix() SendPreferencesToMatrix()
SendMessage(roomID, msgtype, message string) (string, error) SendMessage(roomID string, msgtype gomatrix.MessageType, message string) (string, error)
SendMarkdownMessage(roomID, msgtype, message string) (string, error) SendMarkdownMessage(roomID string, msgtype gomatrix.MessageType, message string) (string, error)
SendTyping(roomID string, typing bool) SendTyping(roomID string, typing bool)
MarkRead(roomID, eventID string) MarkRead(roomID, eventID string)
JoinRoom(roomID, server string) (*rooms.Room, error) JoinRoom(roomID, server string) (*rooms.Room, error)
LeaveRoom(roomID string) error LeaveRoom(roomID string) error
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) GetHistory(roomID, prevBatch string, limit int) ([]*gomatrix.Event, string, error)
GetRoom(roomID string) *rooms.Room GetRoom(roomID string) *rooms.Room
Download(mxcURL string) ([]byte, string, string, error) Download(mxcURL string) ([]byte, string, string, error)

View File

@ -73,8 +73,8 @@ type RoomView interface {
SetTyping(users []string) SetTyping(users []string)
UpdateUserList() UpdateUserList()
NewMessage(id, sender, msgtype, text string, timestamp time.Time) Message NewMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) Message
NewTempMessage(msgtype, text string) Message NewTempMessage(msgtype gomatrix.MessageType, text string) Message
AddMessage(message Message, direction MessageDirection) AddMessage(message Message, direction MessageDirection)
AddServiceMessage(message string) AddServiceMessage(message string)
} }
@ -111,8 +111,8 @@ type Message interface {
SetID(id string) SetID(id string)
ID() string ID() string
SetType(msgtype string) SetType(msgtype gomatrix.MessageType)
Type() string Type() gomatrix.MessageType
NotificationContent() string NotificationContent() string

View File

@ -175,6 +175,8 @@ func (c *Container) PushRules() *pushrules.PushRuleset {
return c.config.PushRules return c.config.PushRules
} }
var AccountDataGomuksPreferences = gomatrix.NewEventType("net.maunium.gomuks.preferences")
// OnLogin initializes the syncer and updates the room list. // OnLogin initializes the syncer and updates the room list.
func (c *Container) OnLogin() { func (c *Container) OnLogin() {
c.ui.OnLogin() c.ui.OnLogin()
@ -183,14 +185,14 @@ func (c *Container) OnLogin() {
debug.Print("Initializing syncer") debug.Print("Initializing syncer")
c.syncer = NewGomuksSyncer(c.config) c.syncer = NewGomuksSyncer(c.config)
c.syncer.OnEventType("m.room.message", c.HandleMessage) c.syncer.OnEventType(gomatrix.EventMessage, c.HandleMessage)
c.syncer.OnEventType("m.room.member", c.HandleMembership) c.syncer.OnEventType(gomatrix.StateMember, c.HandleMembership)
c.syncer.OnEventType("m.receipt", c.HandleReadReceipt) c.syncer.OnEventType(gomatrix.EphemeralEventReceipt, c.HandleReadReceipt)
c.syncer.OnEventType("m.typing", c.HandleTyping) c.syncer.OnEventType(gomatrix.EphemeralEventTyping, c.HandleTyping)
c.syncer.OnEventType("m.direct", c.HandleDirectChatInfo) c.syncer.OnEventType(gomatrix.AccountDataDirectChats, c.HandleDirectChatInfo)
c.syncer.OnEventType("m.push_rules", c.HandlePushRules) c.syncer.OnEventType(gomatrix.AccountDataPushRules, c.HandlePushRules)
c.syncer.OnEventType("m.tag", c.HandleTag) c.syncer.OnEventType(gomatrix.AccountDataRoomTags, c.HandleTag)
c.syncer.OnEventType("net.maunium.gomuks.preferences", c.HandlePreferences) c.syncer.OnEventType(AccountDataGomuksPreferences, c.HandlePreferences)
c.syncer.InitDoneCallback = func() { c.syncer.InitDoneCallback = func() {
c.config.AuthCache.InitialSyncDone = true c.config.AuthCache.InitialSyncDone = true
c.config.SaveAuthCache() c.config.SaveAuthCache()
@ -301,10 +303,10 @@ func (c *Container) HandleMembership(source EventSource, evt *gomatrix.Event) {
} }
func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) { func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) {
membership, _ := evt.Content["membership"].(string) membership := evt.Content.Membership
prevMembership := "leave" prevMembership := gomatrix.MembershipLeave
if evt.Unsigned.PrevContent != nil { if evt.Unsigned.PrevContent != nil {
prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) prevMembership = evt.Unsigned.PrevContent.Membership
} }
debug.Printf("Processing own membership change: %s->%s in %s", prevMembership, membership, evt.RoomID) debug.Printf("Processing own membership change: %s->%s in %s", prevMembership, membership, evt.RoomID)
if membership == prevMembership { if membership == prevMembership {
@ -326,7 +328,7 @@ func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) {
func (c *Container) parseReadReceipt(evt *gomatrix.Event) (largestTimestampEvent string) { func (c *Container) parseReadReceipt(evt *gomatrix.Event) (largestTimestampEvent string) {
var largestTimestamp int64 var largestTimestamp int64
for eventID, rawContent := range evt.Content { for eventID, rawContent := range evt.Content.Raw {
content, ok := rawContent.(map[string]interface{}) content, ok := rawContent.(map[string]interface{})
if !ok { if !ok {
continue continue
@ -368,7 +370,7 @@ func (c *Container) HandleReadReceipt(source EventSource, evt *gomatrix.Event) {
func (c *Container) parseDirectChatInfo(evt *gomatrix.Event) map[*rooms.Room]bool { func (c *Container) parseDirectChatInfo(evt *gomatrix.Event) map[*rooms.Room]bool {
directChats := make(map[*rooms.Room]bool) directChats := make(map[*rooms.Room]bool)
for _, rawRoomIDList := range evt.Content { for _, rawRoomIDList := range evt.Content.Raw {
roomIDList, ok := rawRoomIDList.([]interface{}) roomIDList, ok := rawRoomIDList.([]interface{})
if !ok { if !ok {
continue continue
@ -416,15 +418,12 @@ func (c *Container) HandlePushRules(source EventSource, evt *gomatrix.Event) {
func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) { func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) {
room := c.config.GetRoom(evt.RoomID) room := c.config.GetRoom(evt.RoomID)
tags, _ := evt.Content["tags"].(map[string]interface{}) newTags := make([]rooms.RoomTag, len(evt.Content.RoomTags))
newTags := make([]rooms.RoomTag, len(tags))
index := 0 index := 0
for tag, infoifc := range tags { for tag, info := range evt.Content.RoomTags {
info, _ := infoifc.(map[string]interface{})
order := "0.5" order := "0.5"
rawOrder, ok := info["order"] if len(info.Order) > 0 {
if ok { order = info.Order
order = fmt.Sprintf("%v", rawOrder)
} }
newTags[index] = rooms.RoomTag{ newTags[index] = rooms.RoomTag{
Tag: tag, Tag: tag,
@ -440,13 +439,7 @@ func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) {
// HandleTyping is the event handler for the m.typing event. // HandleTyping is the event handler for the m.typing event.
func (c *Container) HandleTyping(source EventSource, evt *gomatrix.Event) { func (c *Container) HandleTyping(source EventSource, evt *gomatrix.Event) {
users := evt.Content["user_ids"].([]interface{}) c.ui.MainView().SetTyping(evt.RoomID, evt.Content.TypingUserIDs)
strUsers := make([]string, len(users))
for i, user := range users {
strUsers[i] = user.(string)
}
c.ui.MainView().SetTyping(evt.RoomID, strUsers)
} }
func (c *Container) MarkRead(roomID, eventID string) { func (c *Container) MarkRead(roomID, eventID string) {
@ -455,11 +448,11 @@ func (c *Container) MarkRead(roomID, eventID string) {
} }
// SendMessage sends a message with the given text to the given room. // SendMessage sends a message with the given text to the given room.
func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) { func (c *Container) SendMessage(roomID string, msgtype gomatrix.MessageType, text string) (string, error) {
defer debug.Recover() defer debug.Recover()
c.SendTyping(roomID, false) c.SendTyping(roomID, false)
resp, err := c.client.SendMessageEvent(roomID, "m.room.message", resp, err := c.client.SendMessageEvent(roomID, gomatrix.EventMessage,
gomatrix.TextMessage{MsgType: msgtype, Body: text}) gomatrix.Content{MsgType: msgtype, Body: text})
if err != nil { if err != nil {
return "", err return "", err
} }
@ -498,7 +491,7 @@ var roomRegex = regexp.MustCompile("\\[.+?]\\(https://matrix.to/#/(#.+?:[^/]+?)\
// //
// If the given text contains markdown formatting symbols, it will be rendered into HTML before sending. // If the given text contains markdown formatting symbols, it will be rendered into HTML before sending.
// Otherwise, it will be sent as plain text. // Otherwise, it will be sent as plain text.
func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, error) { func (c *Container) SendMarkdownMessage(roomID string, msgtype gomatrix.MessageType, text string) (string, error) {
defer debug.Recover() defer debug.Recover()
html := c.renderMarkdown(text) html := c.renderMarkdown(text)
@ -511,12 +504,12 @@ func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, e
text = roomRegex.ReplaceAllString(text, "$1") text = roomRegex.ReplaceAllString(text, "$1")
c.SendTyping(roomID, false) c.SendTyping(roomID, false)
resp, err := c.client.SendMessageEvent(roomID, "m.room.message", resp, err := c.client.SendMessageEvent(roomID, gomatrix.EventMessage,
map[string]interface{}{ gomatrix.Content{
"msgtype": msgtype, MsgType: msgtype,
"body": text, Body: text,
"format": "org.matrix.custom.html", Format: gomatrix.FormatHTML,
"formatted_body": html, FormattedBody: html,
}) })
if err != nil { if err != nil {
return "", err return "", err
@ -567,7 +560,7 @@ func (c *Container) LeaveRoom(roomID string) error {
} }
// GetHistory fetches room history. // GetHistory fetches room history.
func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) { func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]*gomatrix.Event, string, error) {
resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit) resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit)
if err != nil { if err != nil {
return nil, "", err return nil, "", err

View File

@ -23,14 +23,13 @@ import (
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
"maunium.net/go/gomuks/lib/glob" "maunium.net/go/gomuks/lib/glob"
"maunium.net/go/gomuks/matrix/rooms"
) )
// Room is an interface with the functions that are needed for processing room-specific push conditions // Room is an interface with the functions that are needed for processing room-specific push conditions
type Room interface { type Room interface {
GetMember(mxid string) *rooms.Member GetMember(mxid string) *gomatrix.Member
GetMembers() map[string]*rooms.Member GetMembers() map[string]*gomatrix.Member
GetSessionOwner() *rooms.Member GetSessionOwner() string
} }
// PushCondKind is the type of a push condition. // PushCondKind is the type of a push condition.
@ -89,7 +88,7 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool {
switch key { switch key {
case "type": case "type":
return pattern.MatchString(event.Type) return pattern.MatchString(event.Type.String())
case "sender": case "sender":
return pattern.MatchString(event.Sender) return pattern.MatchString(event.Sender)
case "room_id": case "room_id":
@ -100,7 +99,7 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool {
} }
return pattern.MatchString(*event.StateKey) return pattern.MatchString(*event.StateKey)
case "content": case "content":
val, _ := event.Content[subkey].(string) val, _ := event.Content.Raw[subkey].(string)
return pattern.MatchString(val) return pattern.MatchString(val)
default: default:
return false return false
@ -108,12 +107,12 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool {
} }
func (cond *PushCondition) matchDisplayName(room Room, event *gomatrix.Event) bool { func (cond *PushCondition) matchDisplayName(room Room, event *gomatrix.Event) bool {
member := room.GetSessionOwner() ownerID := room.GetSessionOwner()
if member == nil || member.UserID == event.Sender { if ownerID == event.Sender {
return false return false
} }
text, _ := event.Content["body"].(string) member := room.GetMember(ownerID)
return strings.Contains(text, member.DisplayName) return strings.Contains(event.Content.Body, member.Displayname)
} }
func (cond *PushCondition) matchMemberCount(room Room, event *gomatrix.Event) bool { func (cond *PushCondition) matchMemberCount(room Room, event *gomatrix.Event) bool {

View File

@ -21,19 +21,17 @@ func GetScopedPushRules(client *gomatrix.Client, scope string) (resp *PushRulese
return return
} }
type contentWithRuleset struct {
Ruleset *PushRuleset `json:"global"`
}
// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON. // EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) { func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) {
content, _ := event.Content["global"] content := &contentWithRuleset{}
raw, err := json.Marshal(content) err := json.Unmarshal(event.Content.VeryRaw, content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ruleset := &PushRuleset{} return content.Ruleset, nil
err = json.Unmarshal(raw, ruleset)
if err != nil {
return nil, err
}
return ruleset, nil
} }

View File

@ -154,6 +154,5 @@ func (rule *PushRule) matchPattern(room Room, event *gomatrix.Event) bool {
if err != nil { if err != nil {
return false return false
} }
text, _ := event.Content["body"].(string) return pattern.MatchString(event.Content.Body)
return pattern.MatchString(text)
} }

View File

@ -1,63 +0,0 @@
// gomuks - A terminal Matrix client written in Go.
// Copyright (C) 2018 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package rooms
import (
"maunium.net/go/gomatrix"
)
// Membership is an enum specifying the membership state of a room member.
type Membership string
// The allowed membership states as specified in spec section 10.5.5.
const (
MembershipJoin Membership = "join"
MembershipLeave Membership = "leave"
MembershipInvite Membership = "invite"
MembershipBan Membership = "ban"
MembershipKnock Membership = "knock"
)
// Member represents a member in a room.
type Member struct {
// The MXID of the member.
UserID string `json:"-"`
// The membership status. Defaults to leave.
Membership Membership `json:"membership"`
// The display name of the user. Defaults to the user ID.
DisplayName string `json:"displayname"`
// The avatar URL of the user. Defaults to an empty string.
AvatarURL string `json:"avatar_url"`
}
// eventToRoomMember converts a m.room.member state event into a Member object.
func eventToRoomMember(userID string, event *gomatrix.Event) *Member {
membership, _ := event.Content["membership"].(string)
avatarURL, _ := event.Content["avatar_url"].(string)
displayName, _ := event.Content["displayname"].(string)
if len(displayName) == 0 {
displayName = userID
}
return &Member{
UserID: userID,
Membership: Membership(membership),
DisplayName: displayName,
AvatarURL: avatarURL,
}
}

View File

@ -1,17 +0,0 @@
// gomuks - A terminal Matrix client written in Go.
// Copyright (C) 2018 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package rooms_test

View File

@ -82,10 +82,10 @@ type Room struct {
LastReceivedMessage time.Time LastReceivedMessage time.Time
// MXID -> Member cache calculated from membership events. // MXID -> Member cache calculated from membership events.
memberCache map[string]*Member memberCache map[string]*gomatrix.Member
// The first non-SessionUserID member in the room. Calculated at // The first non-SessionUserID member in the room. Calculated at
// the same time as memberCache. // the same time as memberCache.
firstMemberCache *Member firstMemberCache *gomatrix.Member
// The name of the room. Calculated from the state event name, // The name of the room. Calculated from the state event name,
// canonical_alias or alias or the member cache. // canonical_alias or alias or the member cache.
nameCache string nameCache string
@ -222,25 +222,25 @@ func (room *Room) UpdateState(event *gomatrix.Event) {
room.State[event.Type] = make(map[string]*gomatrix.Event) room.State[event.Type] = make(map[string]*gomatrix.Event)
} }
switch event.Type { switch event.Type {
case "m.room.name": case gomatrix.StateRoomName:
room.nameCache = "" room.nameCache = ""
case "m.room.canonical_alias": case gomatrix.StateCanonicalAlias:
if room.nameCacheSource >= CanonicalAliasRoomName { if room.nameCacheSource >= CanonicalAliasRoomName {
room.nameCache = "" room.nameCache = ""
} }
room.canonicalAliasCache = "" room.canonicalAliasCache = ""
case "m.room.aliases": case gomatrix.StateAliases:
if room.nameCacheSource >= AliasRoomName { if room.nameCacheSource >= AliasRoomName {
room.nameCache = "" room.nameCache = ""
} }
room.aliasesCache = nil room.aliasesCache = nil
case "m.room.member": case gomatrix.StateMember:
room.memberCache = nil room.memberCache = nil
room.firstMemberCache = nil room.firstMemberCache = nil
if room.nameCacheSource >= MemberRoomName { if room.nameCacheSource >= MemberRoomName {
room.nameCache = "" room.nameCache = ""
} }
case "m.room.topic": case gomatrix.StateTopic:
room.topicCache = "" room.topicCache = ""
} }
@ -248,7 +248,7 @@ func (room *Room) UpdateState(event *gomatrix.Event) {
if event.StateKey != nil { if event.StateKey != nil {
stateKey = *event.StateKey stateKey = *event.StateKey
} }
if event.Type != "m.room.member" { if event.Type != gomatrix.StateMember {
debug.Printf("Updating state %s#%s for %s", event.Type, stateKey, room.ID) debug.Printf("Updating state %s#%s for %s", event.Type, stateKey, room.ID)
} }
@ -260,14 +260,14 @@ func (room *Room) UpdateState(event *gomatrix.Event) {
} }
// GetStateEvent returns the state event for the given type/state_key combo, or nil. // GetStateEvent returns the state event for the given type/state_key combo, or nil.
func (room *Room) GetStateEvent(eventType string, stateKey string) *gomatrix.Event { func (room *Room) GetStateEvent(eventType gomatrix.EventType, stateKey string) *gomatrix.Event {
stateEventMap, _ := room.State[eventType] stateEventMap, _ := room.State[eventType]
event, _ := stateEventMap[stateKey] event, _ := stateEventMap[stateKey]
return event return event
} }
// GetStateEvents returns the state events for the given type. // GetStateEvents returns the state events for the given type.
func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event { func (room *Room) GetStateEvents(eventType gomatrix.EventType) map[string]*gomatrix.Event {
stateEventMap, _ := room.State[eventType] stateEventMap, _ := room.State[eventType]
return stateEventMap return stateEventMap
} }
@ -275,9 +275,9 @@ func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event {
// GetTopic returns the topic of the room. // GetTopic returns the topic of the room.
func (room *Room) GetTopic() string { func (room *Room) GetTopic() string {
if len(room.topicCache) == 0 { if len(room.topicCache) == 0 {
topicEvt := room.GetStateEvent("m.room.topic", "") topicEvt := room.GetStateEvent(gomatrix.StateTopic, "")
if topicEvt != nil { if topicEvt != nil {
room.topicCache, _ = topicEvt.Content["topic"].(string) room.topicCache = topicEvt.Content.Topic
} }
} }
return room.topicCache return room.topicCache
@ -285,9 +285,9 @@ func (room *Room) GetTopic() string {
func (room *Room) GetCanonicalAlias() string { func (room *Room) GetCanonicalAlias() string {
if len(room.canonicalAliasCache) == 0 { if len(room.canonicalAliasCache) == 0 {
canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") canonicalAliasEvt := room.GetStateEvent(gomatrix.StateCanonicalAlias, "")
if canonicalAliasEvt != nil { if canonicalAliasEvt != nil {
room.canonicalAliasCache, _ = canonicalAliasEvt.Content["alias"].(string) room.canonicalAliasCache = canonicalAliasEvt.Content.Alias
} else { } else {
room.canonicalAliasCache = "-" room.canonicalAliasCache = "-"
} }
@ -301,17 +301,10 @@ func (room *Room) GetCanonicalAlias() string {
// GetAliases returns the list of aliases that point to this room. // GetAliases returns the list of aliases that point to this room.
func (room *Room) GetAliases() []string { func (room *Room) GetAliases() []string {
if room.aliasesCache == nil { if room.aliasesCache == nil {
aliasEvents := room.GetStateEvents("m.room.aliases") aliasEvents := room.GetStateEvents(gomatrix.StateAliases)
room.aliasesCache = []string{} room.aliasesCache = []string{}
for _, event := range aliasEvents { for _, event := range aliasEvents {
aliases, _ := event.Content["aliases"].([]interface{}) room.aliasesCache = append(room.aliasesCache, event.Content.Aliases...)
newAliases := make([]string, len(room.aliasesCache)+len(aliases))
copy(newAliases, room.aliasesCache)
for index, alias := range aliases {
newAliases[len(room.aliasesCache)+index], _ = alias.(string)
}
room.aliasesCache = newAliases
} }
} }
return room.aliasesCache return room.aliasesCache
@ -319,9 +312,9 @@ func (room *Room) GetAliases() []string {
// updateNameFromNameEvent updates the room display name to be the name set in the name event. // updateNameFromNameEvent updates the room display name to be the name set in the name event.
func (room *Room) updateNameFromNameEvent() { func (room *Room) updateNameFromNameEvent() {
nameEvt := room.GetStateEvent("m.room.name", "") nameEvt := room.GetStateEvent(gomatrix.StateRoomName, "")
if nameEvt != nil { if nameEvt != nil {
room.nameCache, _ = nameEvt.Content["name"].(string) room.nameCache = nameEvt.Content.Name
} }
} }
@ -353,9 +346,9 @@ func (room *Room) updateNameFromMembers() {
} else if room.firstMemberCache == nil { } else if room.firstMemberCache == nil {
room.nameCache = "Room" room.nameCache = "Room"
} else if len(members) == 2 { } else if len(members) == 2 {
room.nameCache = room.firstMemberCache.DisplayName room.nameCache = room.firstMemberCache.Displayname
} else { } else {
firstMember := room.firstMemberCache.DisplayName firstMember := room.firstMemberCache.Displayname
room.nameCache = fmt.Sprintf("%s and %d others", firstMember, len(members)-2) room.nameCache = fmt.Sprintf("%s and %d others", firstMember, len(members)-2)
} }
} }
@ -391,18 +384,18 @@ func (room *Room) GetTitle() string {
} }
// createMemberCache caches all member events into a easily processable MXID -> *Member map. // createMemberCache caches all member events into a easily processable MXID -> *Member map.
func (room *Room) createMemberCache() map[string]*Member { func (room *Room) createMemberCache() map[string]*gomatrix.Member {
cache := make(map[string]*Member) cache := make(map[string]*gomatrix.Member)
events := room.GetStateEvents("m.room.member") events := room.GetStateEvents(gomatrix.StateMember)
room.firstMemberCache = nil room.firstMemberCache = nil
if events != nil { if events != nil {
for userID, event := range events { for userID, event := range events {
member := eventToRoomMember(userID, event) member := &event.Content.Member
if room.firstMemberCache == nil && userID != room.SessionUserID { if room.firstMemberCache == nil && userID != room.SessionUserID {
room.firstMemberCache = member room.firstMemberCache = member
} }
if member.Membership != "leave" { if member.Membership != "leave" {
cache[member.UserID] = member cache[userID] = member
} }
} }
} }
@ -414,7 +407,7 @@ func (room *Room) createMemberCache() map[string]*Member {
// //
// The members are returned from the cache. // The members are returned from the cache.
// If the cache is empty, it is updated first. // If the cache is empty, it is updated first.
func (room *Room) GetMembers() map[string]*Member { func (room *Room) GetMembers() map[string]*gomatrix.Member {
if len(room.memberCache) == 0 || room.firstMemberCache == nil { if len(room.memberCache) == 0 || room.firstMemberCache == nil {
room.createMemberCache() room.createMemberCache()
} }
@ -423,7 +416,7 @@ func (room *Room) GetMembers() map[string]*Member {
// GetMember returns the member with the given MXID. // GetMember returns the member with the given MXID.
// If the member doesn't exist, nil is returned. // If the member doesn't exist, nil is returned.
func (room *Room) GetMember(userID string) *Member { func (room *Room) GetMember(userID string) *gomatrix.Member {
if len(room.memberCache) == 0 { if len(room.memberCache) == 0 {
room.createMemberCache() room.createMemberCache()
} }
@ -432,8 +425,8 @@ func (room *Room) GetMember(userID string) *Member {
} }
// GetSessionOwner returns the Member instance of the user whose session this room was created for. // GetSessionOwner returns the Member instance of the user whose session this room was created for.
func (room *Room) GetSessionOwner() *Member { func (room *Room) GetSessionOwner() string {
return room.GetMember(room.SessionUserID) return room.SessionUserID
} }
// NewRoom creates a new Room with the given ID // NewRoom creates a new Room with the given ID

View File

@ -51,7 +51,7 @@ type EventHandler func(source EventSource, event *gomatrix.Event)
// pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information. // pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information.
type GomuksSyncer struct { type GomuksSyncer struct {
Session SyncerSession Session SyncerSession
listeners map[string][]EventHandler // event type to listeners array listeners map[gomatrix.EventType][]EventHandler // event type to listeners array
FirstSyncDone bool FirstSyncDone bool
InitDoneCallback func() InitDoneCallback func()
} }
@ -60,7 +60,7 @@ type GomuksSyncer struct {
func NewGomuksSyncer(session SyncerSession) *GomuksSyncer { func NewGomuksSyncer(session SyncerSession) *GomuksSyncer {
return &GomuksSyncer{ return &GomuksSyncer{
Session: session, Session: session,
listeners: make(map[string][]EventHandler), listeners: make(map[gomatrix.EventType][]EventHandler),
FirstSyncDone: false, FirstSyncDone: false,
} }
} }
@ -114,20 +114,11 @@ func (s *GomuksSyncer) processSyncEvents(room *rooms.Room, events []*gomatrix.Ev
} }
} }
func isState(event *gomatrix.Event) bool {
switch event.Type {
case "m.room.member", "m.room.name", "m.room.topic", "m.room.aliases", "m.room.canonical_alias":
return true
default:
return false
}
}
func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, event *gomatrix.Event, source EventSource) { func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, event *gomatrix.Event, source EventSource) {
if room != nil { if room != nil {
event.RoomID = room.ID event.RoomID = room.ID
} }
if isState(event) { if event.Type.Class == gomatrix.StateEventType {
room.UpdateState(event) room.UpdateState(event)
} }
s.notifyListeners(source, event) s.notifyListeners(source, event)
@ -135,7 +126,7 @@ func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, event *gomatrix.Event,
// OnEventType allows callers to be notified when there are new events for the given event type. // OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks. // There are no duplicate checks.
func (s *GomuksSyncer) OnEventType(eventType string, callback EventHandler) { func (s *GomuksSyncer) OnEventType(eventType gomatrix.EventType, callback EventHandler) {
_, exists := s.listeners[eventType] _, exists := s.listeners[eventType]
if !exists { if !exists {
s.listeners[eventType] = []EventHandler{} s.listeners[eventType] = []EventHandler{}

View File

@ -19,6 +19,7 @@ package ui
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"maunium.net/go/gomatrix"
"strings" "strings"
"unicode" "unicode"
@ -132,7 +133,7 @@ func cmdSendEvent(cmd *Command) {
return return
} }
roomID := cmd.Args[0] roomID := cmd.Args[0]
eventType := cmd.Args[1] eventType := gomatrix.NewEventType(cmd.Args[1])
rawContent := strings.Join(cmd.Args[2:], "") rawContent := strings.Join(cmd.Args[2:], "")
debug.Print(roomID, eventType, rawContent) debug.Print(roomID, eventType, rawContent)
@ -161,7 +162,7 @@ func cmdSetState(cmd *Command) {
} }
roomID := cmd.Args[0] roomID := cmd.Args[0]
eventType := cmd.Args[1] eventType := gomatrix.NewEventType(cmd.Args[1])
stateKey := cmd.Args[2] stateKey := cmd.Args[2]
if stateKey == "-" { if stateKey == "-" {
stateKey = "" stateKey = ""

View File

@ -18,6 +18,7 @@ package messages
import ( import (
"encoding/gob" "encoding/gob"
"maunium.net/go/gomatrix"
"time" "time"
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
@ -33,7 +34,7 @@ func init() {
type BaseMessage struct { type BaseMessage struct {
MsgID string MsgID string
MsgType string MsgType gomatrix.MessageType
MsgSenderID string MsgSenderID string
MsgSender string MsgSender string
MsgSenderColor tcell.Color MsgSenderColor tcell.Color
@ -47,7 +48,7 @@ type BaseMessage struct {
prevPrefs config.UserPreferences prevPrefs config.UserPreferences
} }
func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time) BaseMessage { func newBaseMessage(id, sender, displayname string, msgtype gomatrix.MessageType, timestamp time.Time) BaseMessage {
return BaseMessage{ return BaseMessage{
MsgSenderID: sender, MsgSenderID: sender,
MsgSender: displayname, MsgSender: displayname,
@ -194,11 +195,11 @@ func (msg *BaseMessage) SetID(id string) {
msg.MsgID = id msg.MsgID = id
} }
func (msg *BaseMessage) Type() string { func (msg *BaseMessage) Type() gomatrix.MessageType {
return msg.MsgType return msg.MsgType
} }
func (msg *BaseMessage) SetType(msgtype string) { func (msg *BaseMessage) SetType(msgtype gomatrix.MessageType) {
msg.MsgType = msgtype msg.MsgType = msgtype
} }

View File

@ -18,6 +18,7 @@ package messages
import ( import (
"encoding/gob" "encoding/gob"
"maunium.net/go/gomatrix"
"time" "time"
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
@ -34,7 +35,7 @@ type ExpandedTextMessage struct {
} }
// NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state. // NewExpandedTextMessage creates a new ExpandedTextMessage object with the provided values and the default state.
func NewExpandedTextMessage(id, sender, displayname, msgtype string, text tstring.TString, timestamp time.Time) UIMessage { func NewExpandedTextMessage(id, sender, displayname string, msgtype gomatrix.MessageType, text tstring.TString, timestamp time.Time) UIMessage {
return &ExpandedTextMessage{ return &ExpandedTextMessage{
BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp),
MsgText: text, MsgText: text,

View File

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"maunium.net/go/gomatrix"
"time" "time"
"image/color" "image/color"
@ -47,7 +48,7 @@ type ImageMessage struct {
} }
// NewImageMessage creates a new ImageMessage object with the provided values and the default state. // NewImageMessage creates a new ImageMessage object with the provided values and the default state.
func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname, msgtype, body, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage { func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname string, msgtype gomatrix.MessageType, body, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
return &ImageMessage{ return &ImageMessage{
newBaseMessage(id, sender, displayname, msgtype, timestamp), newBaseMessage(id, sender, displayname, msgtype, timestamp),
body, body,

View File

@ -22,6 +22,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/lucasb-eyer/go-colorful"
"golang.org/x/net/html" "golang.org/x/net/html"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
@ -29,7 +30,6 @@ import (
"maunium.net/go/gomuks/ui/widget" "maunium.net/go/gomuks/ui/widget"
"maunium.net/go/tcell" "maunium.net/go/tcell"
"strconv" "strconv"
"github.com/lucasb-eyer/go-colorful"
) )
var matrixToURL = regexp.MustCompile("^(?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!].*)") var matrixToURL = regexp.MustCompile("^(?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!].*)")
@ -173,7 +173,7 @@ func (parser *htmlParser) linkToTString(node *html.Node, stripLinebreak bool) ts
pillTarget := match[1] pillTarget := match[1]
if pillTarget[0] == '@' { if pillTarget[0] == '@' {
if member := parser.room.GetMember(pillTarget); member != nil { if member := parser.room.GetMember(pillTarget); member != nil {
return tstring.NewColorTString(member.DisplayName, widget.GetHashColor(member.UserID)) return tstring.NewColorTString(member.Displayname, widget.GetHashColor(pillTarget))
} }
} }
return tstring.NewTString(pillTarget) return tstring.NewTString(pillTarget)
@ -271,14 +271,13 @@ func (parser *htmlParser) Parse(htmlData string) tstring.TString {
// ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage. // ParseHTMLMessage parses a HTML-formatted Matrix event into a UIMessage.
func ParseHTMLMessage(room *rooms.Room, evt *gomatrix.Event, senderDisplayname string) tstring.TString { func ParseHTMLMessage(room *rooms.Room, evt *gomatrix.Event, senderDisplayname string) tstring.TString {
htmlData, _ := evt.Content["formatted_body"].(string) htmlData := evt.Content.FormattedBody
htmlData = strings.Replace(htmlData, "\t", " ", -1) htmlData = strings.Replace(htmlData, "\t", " ", -1)
parser := htmlParser{room} parser := htmlParser{room}
str := parser.Parse(htmlData) str := parser.Parse(htmlData)
msgtype, _ := evt.Content["msgtype"].(string) if evt.Content.MsgType == gomatrix.MsgEmote {
if msgtype == "m.emote" {
str = tstring.Join([]tstring.TString{ str = tstring.Join([]tstring.TString{
tstring.NewTString("* "), tstring.NewTString("* "),
tstring.NewColorTString(senderDisplayname, widget.GetHashColor(evt.Sender)), tstring.NewColorTString(senderDisplayname, widget.GetHashColor(evt.Sender)),

View File

@ -33,9 +33,9 @@ import (
func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage { func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
switch evt.Type { switch evt.Type {
case "m.room.message": case gomatrix.EventMessage:
return ParseMessage(matrix, room, evt) return ParseMessage(matrix, room, evt)
case "m.room.member": case gomatrix.StateMember:
return ParseMembershipEvent(room, evt) return ParseMembershipEvent(room, evt)
} }
return nil return nil
@ -53,32 +53,28 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Ev
displayname := evt.Sender displayname := evt.Sender
member := room.GetMember(evt.Sender) member := room.GetMember(evt.Sender)
if member != nil { if member != nil {
displayname = member.DisplayName displayname = member.Displayname
} }
msgtype, _ := evt.Content["msgtype"].(string)
text, _ := evt.Content["body"].(string)
ts := unixToTime(evt.Timestamp) ts := unixToTime(evt.Timestamp)
switch msgtype { switch evt.Content.MsgType {
case "m.text", "m.notice", "m.emote": case "m.text", "m.notice", "m.emote":
format, hasFormat := evt.Content["format"].(string) if evt.Content.Format == gomatrix.FormatHTML {
if hasFormat && format == "org.matrix.custom.html" {
text := ParseHTMLMessage(room, evt, displayname) text := ParseHTMLMessage(room, evt, displayname)
return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) return messages.NewExpandedTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, text, ts)
} }
text = strings.Replace(text, "\t", " ", -1) evt.Content.Body = strings.Replace(evt.Content.Body, "\t", " ", -1)
return messages.NewTextMessage(evt.ID, evt.Sender, displayname, msgtype, text, ts) return messages.NewTextMessage(evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, ts)
case "m.image": case "m.image":
url, _ := evt.Content["url"].(string) data, hs, id, err := matrix.Download(evt.Content.URL)
data, hs, id, err := matrix.Download(url)
if err != nil { if err != nil {
debug.Printf("Failed to download %s: %v", url, err) debug.Printf("Failed to download %s: %v", evt.Content.URL, err)
} }
return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, msgtype, text, hs, id, data, ts) return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, evt.Content.MsgType, evt.Content.Body, hs, id, data, ts)
} }
return nil return nil
} }
func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) { func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership gomatrix.Membership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) {
switch membership { switch membership {
case "invite": case "invite":
sender = "---" sender = "---"
@ -92,12 +88,11 @@ func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership,
case "leave": case "leave":
sender = "<--" sender = "<--"
if evt.Sender != *evt.StateKey { if evt.Sender != *evt.StateKey {
if prevMembership == "ban" { if prevMembership == gomatrix.MembershipBan {
text = tstring.NewColorTString(fmt.Sprintf("%s unbanned %s", senderDisplayname, displayname), tcell.ColorGreen) text = tstring.NewColorTString(fmt.Sprintf("%s unbanned %s", senderDisplayname, displayname), tcell.ColorGreen)
text.Colorize(len(senderDisplayname)+len(" unbanned "), len(displayname), widget.GetHashColor(*evt.StateKey)) text.Colorize(len(senderDisplayname)+len(" unbanned "), len(displayname), widget.GetHashColor(*evt.StateKey))
} else { } else {
reason, _ := evt.Content["reason"].(string) text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed)
text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, reason), tcell.ColorRed)
text.Colorize(len(senderDisplayname)+len(" kicked "), len(displayname), widget.GetHashColor(*evt.StateKey)) text.Colorize(len(senderDisplayname)+len(" kicked "), len(displayname), widget.GetHashColor(*evt.StateKey))
} }
text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender))
@ -109,8 +104,7 @@ func getMembershipChangeMessage(evt *gomatrix.Event, membership, prevMembership,
text.Colorize(0, len(displayname), widget.GetHashColor(*evt.StateKey)) text.Colorize(0, len(displayname), widget.GetHashColor(*evt.StateKey))
} }
case "ban": case "ban":
reason, _ := evt.Content["reason"].(string) text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed)
text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, reason), tcell.ColorRed)
text.Colorize(len(senderDisplayname)+len(" banned "), len(displayname), widget.GetHashColor(*evt.StateKey)) text.Colorize(len(senderDisplayname)+len(" banned "), len(displayname), widget.GetHashColor(*evt.StateKey))
text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender))
} }
@ -121,20 +115,20 @@ func getMembershipEventContent(room *rooms.Room, evt *gomatrix.Event) (sender st
member := room.GetMember(evt.Sender) member := room.GetMember(evt.Sender)
senderDisplayname := evt.Sender senderDisplayname := evt.Sender
if member != nil { if member != nil {
senderDisplayname = member.DisplayName senderDisplayname = member.Displayname
} }
membership, _ := evt.Content["membership"].(string) membership := evt.Content.Membership
displayname, _ := evt.Content["displayname"].(string) displayname := evt.Content.Displayname
if len(displayname) == 0 { if len(displayname) == 0 {
displayname = *evt.StateKey displayname = *evt.StateKey
} }
prevMembership := "leave" prevMembership := gomatrix.MembershipLeave
prevDisplayname := *evt.StateKey prevDisplayname := *evt.StateKey
if evt.Unsigned.PrevContent != nil { if evt.Unsigned.PrevContent != nil {
prevMembership, _ = evt.Unsigned.PrevContent["membership"].(string) prevMembership = evt.Unsigned.PrevContent.Membership
prevDisplayname, _ = evt.Unsigned.PrevContent["displayname"].(string) prevDisplayname = evt.Unsigned.PrevContent.Displayname
if len(prevDisplayname) == 0 { if len(prevDisplayname) == 0 {
prevDisplayname = *evt.StateKey prevDisplayname = *evt.StateKey
} }

View File

@ -19,6 +19,7 @@ package messages
import ( import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"maunium.net/go/gomatrix"
"time" "time"
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
@ -37,7 +38,7 @@ type TextMessage struct {
} }
// NewTextMessage creates a new UITextMessage object with the provided values and the default state. // NewTextMessage creates a new UITextMessage object with the provided values and the default state.
func NewTextMessage(id, sender, displayname, msgtype, text string, timestamp time.Time) UIMessage { func NewTextMessage(id, sender, displayname string, msgtype gomatrix.MessageType, text string, timestamp time.Time) UIMessage {
return &TextMessage{ return &TextMessage{
BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp), BaseMessage: newBaseMessage(id, sender, displayname, msgtype, timestamp),
MsgText: text, MsgText: text,
@ -57,7 +58,7 @@ func (msg *TextMessage) getCache() tstring.TString {
return msg.cache return msg.cache
} }
func (msg *TextMessage) SetType(msgtype string) { func (msg *TextMessage) SetType(msgtype gomatrix.MessageType) {
msg.BaseMessage.SetType(msgtype) msg.BaseMessage.SetType(msgtype)
msg.cache = nil msg.cache = nil
} }

View File

@ -18,6 +18,7 @@ package ui
import ( import (
"fmt" "fmt"
"maunium.net/go/gomatrix"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv" "strconv"
@ -242,7 +243,7 @@ func (view *RoomView) SetTyping(users []string) {
for index, user := range users { for index, user := range users {
member := view.Room.GetMember(user) member := view.Room.GetMember(user)
if member != nil { if member != nil {
users[index] = member.DisplayName users[index] = member.Displayname
} }
} }
view.typing = users view.typing = users
@ -255,14 +256,14 @@ type completion struct {
func (view *RoomView) autocompleteUser(existingText string) (completions []completion) { func (view *RoomView) autocompleteUser(existingText string) (completions []completion) {
textWithoutPrefix := strings.TrimPrefix(existingText, "@") textWithoutPrefix := strings.TrimPrefix(existingText, "@")
for _, user := range view.Room.GetMembers() { for userID, user := range view.Room.GetMembers() {
if user.DisplayName == textWithoutPrefix || user.UserID == existingText { if user.Displayname == textWithoutPrefix || userID == existingText {
// Exact match, return that. // Exact match, return that.
return []completion{{user.DisplayName, user.UserID}} return []completion{{user.Displayname, userID}}
} }
if strings.HasPrefix(user.DisplayName, textWithoutPrefix) || strings.HasPrefix(user.UserID, existingText) { if strings.HasPrefix(user.Displayname, textWithoutPrefix) || strings.HasPrefix(userID, existingText) {
completions = append(completions, completion{user.DisplayName, user.UserID}) completions = append(completions, completion{user.Displayname, userID})
} }
} }
return return
@ -330,12 +331,12 @@ func (view *RoomView) MxRoom() *rooms.Room {
func (view *RoomView) UpdateUserList() { func (view *RoomView) UpdateUserList() {
var joined strings.Builder var joined strings.Builder
var invited strings.Builder var invited strings.Builder
for _, user := range view.Room.GetMembers() { for userID, user := range view.Room.GetMembers() {
if user.Membership == "join" { if user.Membership == "join" {
joined.WriteString(widget.AddColor(user.DisplayName, widget.GetHashColorName(user.UserID))) joined.WriteString(widget.AddColor(user.Displayname, widget.GetHashColorName(userID)))
joined.WriteRune('\n') joined.WriteRune('\n')
} else if user.Membership == "invite" { } else if user.Membership == "invite" {
invited.WriteString(widget.AddColor(user.DisplayName, widget.GetHashColorName(user.UserID))) invited.WriteString(widget.AddColor(user.Displayname, widget.GetHashColorName(userID)))
invited.WriteRune('\n') invited.WriteRune('\n')
} }
} }
@ -346,26 +347,26 @@ func (view *RoomView) UpdateUserList() {
} }
} }
func (view *RoomView) newUIMessage(id, sender, msgtype, text string, timestamp time.Time) messages.UIMessage { func (view *RoomView) newUIMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) messages.UIMessage {
member := view.Room.GetMember(sender) member := view.Room.GetMember(sender)
displayname := sender displayname := sender
if member != nil { if member != nil {
displayname = member.DisplayName displayname = member.Displayname
} }
msg := messages.NewTextMessage(id, sender, displayname, msgtype, text, timestamp) msg := messages.NewTextMessage(id, sender, displayname, msgtype, text, timestamp)
return msg return msg
} }
func (view *RoomView) NewMessage(id, sender, msgtype, text string, timestamp time.Time) ifc.Message { func (view *RoomView) NewMessage(id, sender string, msgtype gomatrix.MessageType, text string, timestamp time.Time) ifc.Message {
return view.newUIMessage(id, sender, msgtype, text, timestamp) return view.newUIMessage(id, sender, msgtype, text, timestamp)
} }
func (view *RoomView) NewTempMessage(msgtype, text string) ifc.Message { func (view *RoomView) NewTempMessage(msgtype gomatrix.MessageType, text string) ifc.Message {
now := time.Now() now := time.Now()
id := strconv.FormatInt(now.UnixNano(), 10) id := strconv.FormatInt(now.UnixNano(), 10)
sender := "" sender := ""
if ownerMember := view.Room.GetSessionOwner(); ownerMember != nil { if ownerMember := view.Room.GetMember(view.Room.GetSessionOwner()); ownerMember != nil {
sender = ownerMember.DisplayName sender = ownerMember.Displayname
} }
message := view.newUIMessage(id, sender, msgtype, text, now) message := view.newUIMessage(id, sender, msgtype, text, now)
message.SetState(ifc.MessageStateSending) message.SetState(ifc.MessageStateSending)

View File

@ -497,7 +497,7 @@ func (view *MainView) LoadHistory(room string) {
} }
roomView.Room.PrevBatch = prevBatch roomView.Room.PrevBatch = prevBatch
for _, evt := range history { for _, evt := range history {
message := view.ParseEvent(roomView, &evt) message := view.ParseEvent(roomView, evt)
if message != nil { if message != nil {
roomView.AddMessage(message, ifc.PrependMessage) roomView.AddMessage(message, ifc.PrependMessage)
} }

View File

@ -2,7 +2,7 @@ ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name> Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies. copyright notice and this permission notice appear in all copies.

View File

@ -16,7 +16,9 @@
// when the code is not running on Google App Engine, compiled by GopherJS, and // when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe" // "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used. // tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe // Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew package spew
@ -34,80 +36,49 @@ const (
ptrSize = unsafe.Sizeof((*byte)(nil)) ptrSize = unsafe.Sizeof((*byte)(nil))
) )
var ( type flag uintptr
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
// internal reflect.Value fields. These values are valid before golang
// commit ecccf07e7f9d which changed the format. The are also valid
// after commit 82f48826c6c7 which changed the format again to mirror
// the original format. Code in the init function updates these offsets
// as necessary.
offsetPtr = uintptr(ptrSize)
offsetScalar = uintptr(0)
offsetFlag = uintptr(ptrSize * 2)
// flagKindWidth and flagKindShift indicate various bits that the var (
// reflect package uses internally to track kind information. // flagRO indicates whether the value field of a reflect.Value
// // is read-only.
// flagRO indicates whether or not the value field of a reflect.Value is flagRO flag
// read-only.
// // flagAddr indicates whether the address of the reflect.Value's
// flagIndir indicates whether the value field of a reflect.Value is // value may be taken.
// the actual data or a pointer to the data. flagAddr flag
//
// These values are valid before golang commit 90a7c3c86944 which
// changed their positions. Code in the init function updates these
// flags as necessary.
flagKindWidth = uintptr(5)
flagKindShift = uintptr(flagKindWidth - 1)
flagRO = uintptr(1 << 0)
flagIndir = uintptr(1 << 1)
) )
func init() { // flagKindMask holds the bits that make up the kind
// Older versions of reflect.Value stored small integers directly in the // part of the flags field. In all the supported versions,
// ptr field (which is named val in the older versions). Versions // it is in the lower 5 bits.
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named const flagKindMask = flag(0x1f)
// scalar for this purpose which unfortunately came before the flag
// field, so the offset of the flag field is different for those
// versions.
//
// This code constructs a new reflect.Value from a known small integer
// and checks if the size of the reflect.Value struct indicates it has
// the scalar field. When it does, the offsets are updated accordingly.
vv := reflect.ValueOf(0xf00)
if unsafe.Sizeof(vv) == (ptrSize * 4) {
offsetScalar = ptrSize * 2
offsetFlag = ptrSize * 3
}
// Commit 90a7c3c86944 changed the flag positions such that the low // Different versions of Go have used different
// order bits are the kind. This code extracts the kind from the flags // bit layouts for the flags type. This table
// field and ensures it's the correct type. When it's not, the flag // records the known combinations.
// order has been changed to the newer format, so the flags are updated var okFlags = []struct {
// accordingly. ro, addr flag
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) }{{
upfv := *(*uintptr)(upf) // From Go 1.4 to 1.5
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift) ro: 1 << 5,
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) { addr: 1 << 7,
flagKindShift = 0 }, {
flagRO = 1 << 5 // Up to Go tip.
flagIndir = 1 << 6 ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
// Commit adf9b30e5594 modified the flags to separate the var flagValOffset = func() uintptr {
// flagRO flag into two bits which specifies whether or not the field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
// field is embedded. This causes flagIndir to move over a bit if !ok {
// and means that flagRO is the combination of either of the panic("reflect.Value has no flag field")
// original flagRO bit and the new bit.
//
// This code detects the change by extracting what used to be
// the indirect bit to ensure it's set. When it's not, the flag
// order has been changed to the newer format, so the flags are
// updated accordingly.
if upfv&flagIndir == 0 {
flagRO = 3 << 5
flagIndir = 1 << 7
}
} }
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
} }
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses // unsafeReflectValue converts the passed reflect.Value into a one that bypasses
@ -119,34 +90,56 @@ func init() {
// This allows us to check for implementations of the Stringer and error // This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and // interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields. // inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { func unsafeReflectValue(v reflect.Value) reflect.Value {
indirects := 1 if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
vt := v.Type() return v
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) }
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) flagFieldPtr := flagField(&v)
if rvf&flagIndir != 0 { *flagFieldPtr &^= flagRO
vt = reflect.PtrTo(v.Type()) *flagFieldPtr |= flagAddr
indirects++ return v
} else if offsetScalar != 0 { }
// The value is in the scalar field when it's not one of the
// reference types. // Sanity checks against future reflect package changes
switch vt.Kind() { // to the type or semantics of the Value.flag field.
case reflect.Uintptr: func init() {
case reflect.Chan: field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
case reflect.Func: if !ok {
case reflect.Map: panic("reflect.Value has no flag field")
case reflect.Ptr: }
case reflect.UnsafePointer: if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
default: panic("reflect.Value flag field has changed kind")
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + }
offsetScalar) type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
} }
} }
panic("reflect.Value read-only flag has changed semantics")
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
} }

View File

@ -16,7 +16,7 @@
// when the code is running on Google App Engine, compiled by GopherJS, or // when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe" // "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used. // tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe // +build js appengine safe disableunsafe !go1.4
package spew package spew

View File

@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
w.Write(closeParenBytes) w.Write(closeParenBytes)
} }
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' // printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w. // prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) { func printHexPtr(w io.Writer, p uintptr) {
// Null pointer. // Null pointer.

View File

@ -35,16 +35,16 @@ var (
// cCharRE is a regular expression that matches a cgo char. // cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them. // It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned // cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump // char. It is used to detect unsigned character arrays to hexdump
// them. // them.
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t. // cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them. // It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
) )
// dumpState contains information about the state of a dump operation. // dumpState contains information about the state of a dump operation.
@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
// Display dereferenced value. // Display dereferenced value.
d.w.Write(openParenBytes) d.w.Write(openParenBytes)
switch { switch {
case nilFound == true: case nilFound:
d.w.Write(nilAngleBytes) d.w.Write(nilAngleBytes)
case cycleFound == true: case cycleFound:
d.w.Write(circularBytes) d.w.Write(circularBytes)
default: default:

View File

@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
// Display dereferenced value. // Display dereferenced value.
switch { switch {
case nilFound == true: case nilFound:
f.fs.Write(nilAngleBytes) f.fs.Write(nilAngleBytes)
case cycleFound == true: case cycleFound:
f.fs.Write(circularShortBytes) f.fs.Write(circularShortBytes)
default: default:

View File

@ -38,9 +38,13 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
parallel(0, src.h, func(ys <-chan int) { parallel(0, src.h, func(ys <-chan int) {
scanLine := make([]uint8, src.w*4) scanLine := make([]uint8, src.w*4)
scanLineF := make([]float64, len(scanLine))
for y := range ys { for y := range ys {
src.scan(0, y, src.w, y+1, scanLine) src.scan(0, y, src.w, y+1, scanLine)
for x := 0; x < src.w; x++ { for i, v := range scanLine {
scanLineF[i] = float64(v)
}
for x, idx := 0, 0; x < src.w; x, idx = x+1, idx+4 {
min := x - radius min := x - radius
if min < 0 { if min < 0 {
min = 0 min = 0
@ -55,10 +59,10 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
i := ix * 4 i := ix * 4
weight := kernel[absint(x-ix)] weight := kernel[absint(x-ix)]
wsum += weight wsum += weight
wa := float64(scanLine[i+3]) * weight wa := scanLineF[i+3] * weight
r += float64(scanLine[i+0]) * wa r += scanLineF[i+0] * wa
g += float64(scanLine[i+1]) * wa g += scanLineF[i+1] * wa
b += float64(scanLine[i+2]) * wa b += scanLineF[i+2] * wa
a += wa a += wa
} }
if a != 0 { if a != 0 {
@ -67,12 +71,12 @@ func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
b /= a b /= a
} }
j := y*dst.Stride + x*4 scanLine[idx+0] = clamp(r)
dst.Pix[j+0] = clamp(r) scanLine[idx+1] = clamp(g)
dst.Pix[j+1] = clamp(g) scanLine[idx+2] = clamp(b)
dst.Pix[j+2] = clamp(b) scanLine[idx+3] = clamp(a / wsum)
dst.Pix[j+3] = clamp(a / wsum)
} }
copy(dst.Pix[y*dst.Stride:], scanLine)
} }
}) })
@ -86,8 +90,12 @@ func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
parallel(0, src.w, func(xs <-chan int) { parallel(0, src.w, func(xs <-chan int) {
scanLine := make([]uint8, src.h*4) scanLine := make([]uint8, src.h*4)
scanLineF := make([]float64, len(scanLine))
for x := range xs { for x := range xs {
src.scan(x, 0, x+1, src.h, scanLine) src.scan(x, 0, x+1, src.h, scanLine)
for i, v := range scanLine {
scanLineF[i] = float64(v)
}
for y := 0; y < src.h; y++ { for y := 0; y < src.h; y++ {
min := y - radius min := y - radius
if min < 0 { if min < 0 {
@ -103,10 +111,10 @@ func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
i := iy * 4 i := iy * 4
weight := kernel[absint(y-iy)] weight := kernel[absint(y-iy)]
wsum += weight wsum += weight
wa := float64(scanLine[i+3]) * weight wa := scanLineF[i+3] * weight
r += float64(scanLine[i+0]) * wa r += scanLineF[i+0] * wa
g += float64(scanLine[i+1]) * wa g += scanLineF[i+1] * wa
b += float64(scanLine[i+2]) * wa b += scanLineF[i+2] * wa
a += wa a += wa
} }
if a != 0 { if a != 0 {

View File

@ -1,272 +0,0 @@
package imaging
import (
"bytes"
"errors"
"image"
"image/color"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"os"
"path/filepath"
"strings"
"golang.org/x/image/bmp"
"golang.org/x/image/tiff"
)
// Format is an image file format.
type Format int
// Image file formats.
const (
JPEG Format = iota
PNG
GIF
TIFF
BMP
)
func (f Format) String() string {
switch f {
case JPEG:
return "JPEG"
case PNG:
return "PNG"
case GIF:
return "GIF"
case TIFF:
return "TIFF"
case BMP:
return "BMP"
default:
return "Unsupported"
}
}
var formatFromExt = map[string]Format{
".jpg": JPEG,
".jpeg": JPEG,
".png": PNG,
".tif": TIFF,
".tiff": TIFF,
".bmp": BMP,
".gif": GIF,
}
// FormatFromFilename parses image format from filename extension:
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
func FormatFromFilename(filename string) (Format, error) {
ext := strings.ToLower(filepath.Ext(filename))
if f, ok := formatFromExt[ext]; ok {
return f, nil
}
return -1, ErrUnsupportedFormat
}
var (
// ErrUnsupportedFormat means the given image format (or file extension) is unsupported.
ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
)
type fileSystem interface {
Create(string) (io.WriteCloser, error)
Open(string) (io.ReadCloser, error)
}
type localFS struct{}
func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) }
func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
var fs fileSystem = localFS{}
// Decode reads an image from r.
func Decode(r io.Reader) (image.Image, error) {
img, _, err := image.Decode(r)
return img, err
}
// Open loads an image from file
func Open(filename string) (image.Image, error) {
file, err := fs.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
return Decode(file)
}
type encodeConfig struct {
jpegQuality int
gifNumColors int
gifQuantizer draw.Quantizer
gifDrawer draw.Drawer
pngCompressionLevel png.CompressionLevel
}
var defaultEncodeConfig = encodeConfig{
jpegQuality: 95,
gifNumColors: 256,
gifQuantizer: nil,
gifDrawer: nil,
pngCompressionLevel: png.DefaultCompression,
}
// EncodeOption sets an optional parameter for the Encode and Save functions.
type EncodeOption func(*encodeConfig)
// JPEGQuality returns an EncodeOption that sets the output JPEG quality.
// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95.
func JPEGQuality(quality int) EncodeOption {
return func(c *encodeConfig) {
c.jpegQuality = quality
}
}
// GIFNumColors returns an EncodeOption that sets the maximum number of colors
// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256.
func GIFNumColors(numColors int) EncodeOption {
return func(c *encodeConfig) {
c.gifNumColors = numColors
}
}
// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce
// a palette of the GIF-encoded image.
func GIFQuantizer(quantizer draw.Quantizer) EncodeOption {
return func(c *encodeConfig) {
c.gifQuantizer = quantizer
}
}
// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert
// the source image to the desired palette of the GIF-encoded image.
func GIFDrawer(drawer draw.Drawer) EncodeOption {
return func(c *encodeConfig) {
c.gifDrawer = drawer
}
}
// PNGCompressionLevel returns an EncodeOption that sets the compression level
// of the PNG-encoded image. Default is png.DefaultCompression.
func PNGCompressionLevel(level png.CompressionLevel) EncodeOption {
return func(c *encodeConfig) {
c.pngCompressionLevel = level
}
}
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error {
cfg := defaultEncodeConfig
for _, option := range opts {
option(&cfg)
}
var err error
switch format {
case JPEG:
var rgba *image.RGBA
if nrgba, ok := img.(*image.NRGBA); ok {
if nrgba.Opaque() {
rgba = &image.RGBA{
Pix: nrgba.Pix,
Stride: nrgba.Stride,
Rect: nrgba.Rect,
}
}
}
if rgba != nil {
err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality})
} else {
err = jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality})
}
case PNG:
enc := png.Encoder{CompressionLevel: cfg.pngCompressionLevel}
err = enc.Encode(w, img)
case GIF:
err = gif.Encode(w, img, &gif.Options{
NumColors: cfg.gifNumColors,
Quantizer: cfg.gifQuantizer,
Drawer: cfg.gifDrawer,
})
case TIFF:
err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
case BMP:
err = bmp.Encode(w, img)
default:
err = ErrUnsupportedFormat
}
return err
}
// Save saves the image to file with the specified filename.
// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
//
// Examples:
//
// // Save the image as PNG.
// err := imaging.Save(img, "out.png")
//
// // Save the image as JPEG with optional quality parameter set to 80.
// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80))
//
func Save(img image.Image, filename string, opts ...EncodeOption) (err error) {
f, err := FormatFromFilename(filename)
if err != nil {
return err
}
file, err := fs.Create(filename)
if err != nil {
return err
}
defer func() {
cerr := file.Close()
if err == nil {
err = cerr
}
}()
return Encode(file, img, f, opts...)
}
// New creates a new image with the specified width and height, and fills it with the specified color.
func New(width, height int, fillColor color.Color) *image.NRGBA {
if width <= 0 || height <= 0 {
return &image.NRGBA{}
}
c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
if (c == color.NRGBA{0, 0, 0, 0}) {
return image.NewNRGBA(image.Rect(0, 0, width, height))
}
return &image.NRGBA{
Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height),
Stride: 4 * width,
Rect: image.Rect(0, 0, width, height),
}
}
// Clone returns a copy of the given image.
func Clone(img image.Image) *image.NRGBA {
src := newScanner(img)
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
size := src.w * 4
parallel(0, src.h, func(ys <-chan int) {
for y := range ys {
i := y * dst.Stride
src.scan(0, y, src.w, y+1, dst.Pix[i:i+size])
}
})
return dst
}

463
vendor/github.com/disintegration/imaging/io.go generated vendored Normal file
View File

@ -0,0 +1,463 @@
package imaging
import (
"encoding/binary"
"errors"
"image"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/image/bmp"
"golang.org/x/image/tiff"
)
// Format is an image file format.
type Format int
// Image file formats.
const (
JPEG Format = iota
PNG
GIF
TIFF
BMP
)
func (f Format) String() string {
switch f {
case JPEG:
return "JPEG"
case PNG:
return "PNG"
case GIF:
return "GIF"
case TIFF:
return "TIFF"
case BMP:
return "BMP"
default:
return "Unsupported"
}
}
var formatFromExt = map[string]Format{
"jpg": JPEG,
"jpeg": JPEG,
"png": PNG,
"tif": TIFF,
"tiff": TIFF,
"bmp": BMP,
"gif": GIF,
}
// FormatFromExtension parses image format from extension:
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
func FormatFromExtension(ext string) (Format, error) {
if f, ok := formatFromExt[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok {
return f, nil
}
return -1, ErrUnsupportedFormat
}
// FormatFromFilename parses image format from filename extension:
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
func FormatFromFilename(filename string) (Format, error) {
ext := filepath.Ext(filename)
return FormatFromExtension(ext)
}
var (
// ErrUnsupportedFormat means the given image format (or file extension) is unsupported.
ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
)
type fileSystem interface {
Create(string) (io.WriteCloser, error)
Open(string) (io.ReadCloser, error)
}
type localFS struct{}
func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) }
func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
var fs fileSystem = localFS{}
type decodeConfig struct {
autoOrientation bool
}
var defaultDecodeConfig = decodeConfig{
autoOrientation: false,
}
// DecodeOption sets an optional parameter for the Decode and Open functions.
type DecodeOption func(*decodeConfig)
// AutoOrientation returns a DecodeOption that sets the auto-orientation mode.
// If auto-orientation is enabled, the image will be transformed after decoding
// according to the EXIF orientation tag (if present). By default it's disabled.
func AutoOrientation(enabled bool) DecodeOption {
return func(c *decodeConfig) {
c.autoOrientation = enabled
}
}
// Decode reads an image from r.
func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) {
cfg := defaultDecodeConfig
for _, option := range opts {
option(&cfg)
}
if !cfg.autoOrientation {
img, _, err := image.Decode(r)
return img, err
}
var orient orientation
pr, pw := io.Pipe()
r = io.TeeReader(r, pw)
done := make(chan struct{})
go func() {
defer close(done)
orient = readOrientation(pr)
io.Copy(ioutil.Discard, pr)
}()
img, _, err := image.Decode(r)
pw.Close()
<-done
if err != nil {
return nil, err
}
return fixOrientation(img, orient), nil
}
// Open loads an image from file.
//
// Examples:
//
// // Load an image from file.
// img, err := imaging.Open("test.jpg")
//
// // Load an image and transform it depending on the EXIF orientation tag (if present).
// img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
//
func Open(filename string, opts ...DecodeOption) (image.Image, error) {
file, err := fs.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
return Decode(file, opts...)
}
type encodeConfig struct {
jpegQuality int
gifNumColors int
gifQuantizer draw.Quantizer
gifDrawer draw.Drawer
pngCompressionLevel png.CompressionLevel
}
var defaultEncodeConfig = encodeConfig{
jpegQuality: 95,
gifNumColors: 256,
gifQuantizer: nil,
gifDrawer: nil,
pngCompressionLevel: png.DefaultCompression,
}
// EncodeOption sets an optional parameter for the Encode and Save functions.
type EncodeOption func(*encodeConfig)
// JPEGQuality returns an EncodeOption that sets the output JPEG quality.
// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95.
func JPEGQuality(quality int) EncodeOption {
return func(c *encodeConfig) {
c.jpegQuality = quality
}
}
// GIFNumColors returns an EncodeOption that sets the maximum number of colors
// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256.
func GIFNumColors(numColors int) EncodeOption {
return func(c *encodeConfig) {
c.gifNumColors = numColors
}
}
// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce
// a palette of the GIF-encoded image.
func GIFQuantizer(quantizer draw.Quantizer) EncodeOption {
return func(c *encodeConfig) {
c.gifQuantizer = quantizer
}
}
// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert
// the source image to the desired palette of the GIF-encoded image.
func GIFDrawer(drawer draw.Drawer) EncodeOption {
return func(c *encodeConfig) {
c.gifDrawer = drawer
}
}
// PNGCompressionLevel returns an EncodeOption that sets the compression level
// of the PNG-encoded image. Default is png.DefaultCompression.
func PNGCompressionLevel(level png.CompressionLevel) EncodeOption {
return func(c *encodeConfig) {
c.pngCompressionLevel = level
}
}
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error {
cfg := defaultEncodeConfig
for _, option := range opts {
option(&cfg)
}
var err error
switch format {
case JPEG:
var rgba *image.RGBA
if nrgba, ok := img.(*image.NRGBA); ok {
if nrgba.Opaque() {
rgba = &image.RGBA{
Pix: nrgba.Pix,
Stride: nrgba.Stride,
Rect: nrgba.Rect,
}
}
}
if rgba != nil {
err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality})
} else {
err = jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality})
}
case PNG:
enc := png.Encoder{CompressionLevel: cfg.pngCompressionLevel}
err = enc.Encode(w, img)
case GIF:
err = gif.Encode(w, img, &gif.Options{
NumColors: cfg.gifNumColors,
Quantizer: cfg.gifQuantizer,
Drawer: cfg.gifDrawer,
})
case TIFF:
err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
case BMP:
err = bmp.Encode(w, img)
default:
err = ErrUnsupportedFormat
}
return err
}
// Save saves the image to file with the specified filename.
// The format is determined from the filename extension:
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
//
// Examples:
//
// // Save the image as PNG.
// err := imaging.Save(img, "out.png")
//
// // Save the image as JPEG with optional quality parameter set to 80.
// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80))
//
func Save(img image.Image, filename string, opts ...EncodeOption) (err error) {
f, err := FormatFromFilename(filename)
if err != nil {
return err
}
file, err := fs.Create(filename)
if err != nil {
return err
}
defer func() {
cerr := file.Close()
if err == nil {
err = cerr
}
}()
return Encode(file, img, f, opts...)
}
// orientation is an EXIF flag that specifies the transformation
// that should be applied to image to display it correctly.
type orientation int
const (
orientationUnspecified = 0
orientationNormal = 1
orientationFlipH = 2
orientationRotate180 = 3
orientationFlipV = 4
orientationTranspose = 5
orientationRotate270 = 6
orientationTransverse = 7
orientationRotate90 = 8
)
// readOrientation tries to read the orientation EXIF flag from image data in r.
// If the EXIF data block is not found or the orientation flag is not found
// or any other error occures while reading the data, it returns the
// orientationUnspecified (0) value.
func readOrientation(r io.Reader) orientation {
const (
markerSOI = 0xffd8
markerAPP1 = 0xffe1
exifHeader = 0x45786966
byteOrderBE = 0x4d4d
byteOrderLE = 0x4949
orientationTag = 0x0112
)
// Check if JPEG SOI marker is present.
var soi uint16
if err := binary.Read(r, binary.BigEndian, &soi); err != nil {
return orientationUnspecified
}
if soi != markerSOI {
return orientationUnspecified // Missing JPEG SOI marker.
}
// Find JPEG APP1 marker.
for {
var marker, size uint16
if err := binary.Read(r, binary.BigEndian, &marker); err != nil {
return orientationUnspecified
}
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return orientationUnspecified
}
if marker>>8 != 0xff {
return orientationUnspecified // Invalid JPEG marker.
}
if marker == markerAPP1 {
break
}
if size < 2 {
return orientationUnspecified // Invalid block size.
}
if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil {
return orientationUnspecified
}
}
// Check if EXIF header is present.
var header uint32
if err := binary.Read(r, binary.BigEndian, &header); err != nil {
return orientationUnspecified
}
if header != exifHeader {
return orientationUnspecified
}
if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
return orientationUnspecified
}
// Read byte order information.
var (
byteOrderTag uint16
byteOrder binary.ByteOrder
)
if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil {
return orientationUnspecified
}
switch byteOrderTag {
case byteOrderBE:
byteOrder = binary.BigEndian
case byteOrderLE:
byteOrder = binary.LittleEndian
default:
return orientationUnspecified // Invalid byte order flag.
}
if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
return orientationUnspecified
}
// Skip the EXIF offset.
var offset uint32
if err := binary.Read(r, byteOrder, &offset); err != nil {
return orientationUnspecified
}
if offset < 8 {
return orientationUnspecified // Invalid offset value.
}
if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil {
return orientationUnspecified
}
// Read the number of tags.
var numTags uint16
if err := binary.Read(r, byteOrder, &numTags); err != nil {
return orientationUnspecified
}
// Find the orientation tag.
for i := 0; i < int(numTags); i++ {
var tag uint16
if err := binary.Read(r, byteOrder, &tag); err != nil {
return orientationUnspecified
}
if tag != orientationTag {
if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil {
return orientationUnspecified
}
continue
}
if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil {
return orientationUnspecified
}
var val uint16
if err := binary.Read(r, byteOrder, &val); err != nil {
return orientationUnspecified
}
if val < 1 || val > 8 {
return orientationUnspecified // Invalid tag value.
}
return orientation(val)
}
return orientationUnspecified // Missing orientation tag.
}
// fixOrientation applies a transform to img corresponding to the given orientation flag.
func fixOrientation(img image.Image, o orientation) image.Image {
switch o {
case orientationNormal:
case orientationFlipH:
img = FlipH(img)
case orientationFlipV:
img = FlipV(img)
case orientationRotate90:
img = Rotate90(img)
case orientationRotate180:
img = Rotate180(img)
case orientationRotate270:
img = Rotate270(img)
case orientationTranspose:
img = Transpose(img)
case orientationTransverse:
img = Transverse(img)
}
return img
}

View File

@ -1,10 +1,44 @@
package imaging package imaging
import ( import (
"bytes"
"image" "image"
"image/color"
"math" "math"
) )
// New creates a new image with the specified width and height, and fills it with the specified color.
func New(width, height int, fillColor color.Color) *image.NRGBA {
if width <= 0 || height <= 0 {
return &image.NRGBA{}
}
c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
if (c == color.NRGBA{0, 0, 0, 0}) {
return image.NewNRGBA(image.Rect(0, 0, width, height))
}
return &image.NRGBA{
Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height),
Stride: 4 * width,
Rect: image.Rect(0, 0, width, height),
}
}
// Clone returns a copy of the given image.
func Clone(img image.Image) *image.NRGBA {
src := newScanner(img)
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
size := src.w * 4
parallel(0, src.h, func(ys <-chan int) {
for y := range ys {
i := y * dst.Stride
src.scan(0, y, src.w, y+1, dst.Pix[i:i+size])
}
})
return dst
}
// Anchor is the anchor point for image alignment. // Anchor is the anchor point for image alignment.
type Anchor int type Anchor int

View File

@ -124,17 +124,16 @@ Because a `colorful.Color` implements Go's `color.Color` interface (found in the
`image/color` package), it can be used anywhere that expects a `color.Color`. `image/color` package), it can be used anywhere that expects a `color.Color`.
Furthermore, you can convert anything that implements the `color.Color` interface Furthermore, you can convert anything that implements the `color.Color` interface
into a `colorful.Color` using the `MakeColorful` function: into a `colorful.Color` using the `MakeColor` function:
```go ```go
c := colorful.MakeColor(color.Gray16{12345}) c, ok := colorful.MakeColor(color.Gray16{12345})
``` ```
**Caveat:** Be aware that for this latter conversion (using `MakeColor`) hits a corner-case **Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a
when alpha is exactly zero. Because `color.Color` uses pre-multiplied alpha colors, corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied
this means the RGB values are lost and it's impossible to recover them. The current alpha colors, this means the RGB values are lost (set to 0) and it's impossible
implementation `panic()`s in that case, see [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) to recover them. In such a case `MakeColor` will return `false` as its second value.
for discussion and suggesting alternatives.
### Comparing colors ### Comparing colors
In the RGB color space, the Euclidian distance between colors *doesn't* correspond In the RGB color space, the Euclidian distance between colors *doesn't* correspond
@ -456,16 +455,26 @@ but that is outside the scope of this library.
Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation, Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation,
see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details. see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details.
### Q: `MakeColor` panics when alpha is zero! ### Q: Why would `MakeColor` ever fail!?
A: Because in that case, the conversion is undefined. See A: `MakeColor` fails when the alpha channel is zero. In that case, the
[issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21)
as well as the short caveat discussion in the ["The `color.Color` interface"](README.md#the-colorcolor-interface) section above. as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface)
section above.
Who? Who?
==== ====
This library has been developed by Lucas Beyer with contributions from This library has been developed by Lucas Beyer with contributions from
Bastien Dejean (@baskerville) and Phil Kulak (@pkulak). Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli).
Release Notes
=============
### Version 1.0
- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](https://github.com/lucasb-eyer/go-colorful#the-colorcolor-interface) section and [this FAQ entry](https://github.com/lucasb-eyer/go-colorful#q-why-would-makecolor-ever-fail) for details.
### Version 0.9
- Initial version number after having ignored versioning for a long time :)
License: MIT License: MIT
============ ============

View File

@ -22,8 +22,11 @@ func (col Color) RGBA() (r, g, b, a uint32) {
} }
// Constructs a colorful.Color from something implementing color.Color // Constructs a colorful.Color from something implementing color.Color
func MakeColor(col color.Color) Color { func MakeColor(col color.Color) (Color, bool) {
r, g, b, a := col.RGBA() r, g, b, a := col.RGBA()
if a == 0 {
return Color{0, 0, 0}, false
}
// Since color.Color is alpha pre-multiplied, we need to divide the // Since color.Color is alpha pre-multiplied, we need to divide the
// RGB values by alpha again in order to get back the original RGB. // RGB values by alpha again in order to get back the original RGB.
@ -34,7 +37,7 @@ func MakeColor(col color.Color) Color {
b *= 0xffff b *= 0xffff
b /= a b /= a
return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0} return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true
} }
// Might come in handy sometimes to reduce boilerplate code. // Might come in handy sometimes to reduce boilerplate code.

View File

@ -1,13 +1,24 @@
package runewidth package runewidth
import "os"
var ( var (
// EastAsianWidth will be set true if the current locale is CJK // EastAsianWidth will be set true if the current locale is CJK
EastAsianWidth = IsEastAsian() EastAsianWidth bool
// DefaultCondition is a condition in current locale // DefaultCondition is a condition in current locale
DefaultCondition = &Condition{EastAsianWidth} DefaultCondition = &Condition{EastAsianWidth}
) )
func init() {
env := os.Getenv("RUNEWIDTH_EASTASIAN")
if env == "" {
EastAsianWidth = IsEastAsian()
} else {
EastAsianWidth = env == "1"
}
}
type interval struct { type interval struct {
first rune first rune
last rune last rune
@ -55,6 +66,7 @@ var private = table{
var nonprint = table{ var nonprint = table{
{0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
{0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
{0x2028, 0x2029},
{0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
} }

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Peter Renström Copyright (c) 2018 Peter Lithammer
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -112,10 +112,7 @@ Outer:
} }
// Count up remaining char // Count up remaining char
for len(target) > 0 { runeDiff += utf8.RuneCountInString(target)
target = target[utf8.RuneLen(rune(target[0])):]
runeDiff++
}
return runeDiff return runeDiff
} }

View File

@ -13,6 +13,9 @@ import (
// Conditionf uses a Comparison to assert a complex condition. // Conditionf uses a Comparison to assert a complex condition.
func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Condition(t, comp, append([]interface{}{msg}, args...)...) return Condition(t, comp, append([]interface{}{msg}, args...)...)
} }
@ -23,11 +26,17 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Contains(t, s, contains, append([]interface{}{msg}, args...)...) return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
} }
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return DirExists(t, path, append([]interface{}{msg}, args...)...) return DirExists(t, path, append([]interface{}{msg}, args...)...)
} }
@ -37,6 +46,9 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
// //
// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
} }
@ -45,6 +57,9 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
// //
// assert.Emptyf(t, obj, "error message %s", "formatted") // assert.Emptyf(t, obj, "error message %s", "formatted")
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Empty(t, object, append([]interface{}{msg}, args...)...) return Empty(t, object, append([]interface{}{msg}, args...)...)
} }
@ -56,6 +71,9 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
// referenced values (as opposed to the memory addresses). Function equality // referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail. // cannot be determined and will always fail.
func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
@ -65,6 +83,9 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
} }
@ -73,6 +94,9 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
// //
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
@ -83,6 +107,9 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
// assert.Equal(t, expectedErrorf, err) // assert.Equal(t, expectedErrorf, err)
// } // }
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Error(t, err, append([]interface{}{msg}, args...)...) return Error(t, err, append([]interface{}{msg}, args...)...)
} }
@ -90,16 +117,25 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
// //
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
// Failf reports a failure through // Failf reports a failure through
func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
} }
// FailNowf fails test // FailNowf fails test
func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
} }
@ -107,31 +143,43 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}
// //
// assert.Falsef(t, myBool, "error message %s", "formatted") // assert.Falsef(t, myBool, "error message %s", "formatted")
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return False(t, value, append([]interface{}{msg}, args...)...) return False(t, value, append([]interface{}{msg}, args...)...)
} }
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FileExists(t, path, append([]interface{}{msg}, args...)...) return FileExists(t, path, append([]interface{}{msg}, args...)...)
} }
// HTTPBodyContainsf asserts that a specified handler returns a // HTTPBodyContainsf asserts that a specified handler returns a
// body that contains a string. // body that contains a string.
// //
// assert.HTTPBodyContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
} }
// HTTPBodyNotContainsf asserts that a specified handler returns a // HTTPBodyNotContainsf asserts that a specified handler returns a
// body that does not contain a string. // body that does not contain a string.
// //
// assert.HTTPBodyNotContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
} }
@ -141,6 +189,9 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
// //
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
} }
@ -150,6 +201,9 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
// //
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
} }
@ -159,6 +213,9 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
} }
@ -166,6 +223,9 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
// //
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
} }
@ -173,31 +233,49 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
// //
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
} }
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
} }
// InDeltaSlicef is the same as InDelta, except it compares two slices. // InDeltaSlicef is the same as InDelta, except it compares two slices.
func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
} }
// InEpsilonf asserts that expected and actual have a relative error less than epsilon // InEpsilonf asserts that expected and actual have a relative error less than epsilon
func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
} }
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
} }
// IsTypef asserts that the specified objects are of the same type. // IsTypef asserts that the specified objects are of the same type.
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
} }
@ -205,6 +283,9 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin
// //
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
@ -213,6 +294,9 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
// //
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") // assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Len(t, object, length, append([]interface{}{msg}, args...)...) return Len(t, object, length, append([]interface{}{msg}, args...)...)
} }
@ -220,6 +304,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
// //
// assert.Nilf(t, err, "error message %s", "formatted") // assert.Nilf(t, err, "error message %s", "formatted")
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Nil(t, object, append([]interface{}{msg}, args...)...) return Nil(t, object, append([]interface{}{msg}, args...)...)
} }
@ -230,6 +317,9 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool
// assert.Equal(t, expectedObj, actualObj) // assert.Equal(t, expectedObj, actualObj)
// } // }
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoError(t, err, append([]interface{}{msg}, args...)...) return NoError(t, err, append([]interface{}{msg}, args...)...)
} }
@ -240,6 +330,9 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
} }
@ -250,6 +343,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
// assert.Equal(t, "two", obj[1]) // assert.Equal(t, "two", obj[1])
// } // }
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEmpty(t, object, append([]interface{}{msg}, args...)...) return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
} }
@ -260,6 +356,9 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
// Pointer variable equality is determined based on the equality of the // Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). // referenced values (as opposed to the memory addresses).
func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
@ -267,6 +366,9 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
// //
// assert.NotNilf(t, err, "error message %s", "formatted") // assert.NotNilf(t, err, "error message %s", "formatted")
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotNil(t, object, append([]interface{}{msg}, args...)...) return NotNil(t, object, append([]interface{}{msg}, args...)...)
} }
@ -274,6 +376,9 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo
// //
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotPanics(t, f, append([]interface{}{msg}, args...)...) return NotPanics(t, f, append([]interface{}{msg}, args...)...)
} }
@ -282,6 +387,9 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
} }
@ -290,11 +398,17 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
// //
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
} }
// NotZerof asserts that i is not the zero value for its type. // NotZerof asserts that i is not the zero value for its type.
func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotZero(t, i, append([]interface{}{msg}, args...)...) return NotZero(t, i, append([]interface{}{msg}, args...)...)
} }
@ -302,6 +416,9 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
// //
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Panics(t, f, append([]interface{}{msg}, args...)...) return Panics(t, f, append([]interface{}{msg}, args...)...)
} }
@ -310,6 +427,9 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
// //
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
} }
@ -318,6 +438,9 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
} }
@ -326,6 +449,9 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
// //
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") // assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Subset(t, list, subset, append([]interface{}{msg}, args...)...) return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
} }
@ -333,6 +459,9 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args
// //
// assert.Truef(t, myBool, "error message %s", "formatted") // assert.Truef(t, myBool, "error message %s", "formatted")
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return True(t, value, append([]interface{}{msg}, args...)...) return True(t, value, append([]interface{}{msg}, args...)...)
} }
@ -340,10 +469,16 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
// //
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
} }
// Zerof asserts that i is the zero value for its type. // Zerof asserts that i is the zero value for its type.
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Zero(t, i, append([]interface{}{msg}, args...)...) return Zero(t, i, append([]interface{}{msg}, args...)...)
} }

View File

@ -1,4 +1,5 @@
{{.CommentFormat}} {{.CommentFormat}}
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
if h, ok := t.(tHelper); ok { h.Helper() }
return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
} }

View File

@ -13,11 +13,17 @@ import (
// Condition uses a Comparison to assert a complex condition. // Condition uses a Comparison to assert a complex condition.
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Condition(a.t, comp, msgAndArgs...) return Condition(a.t, comp, msgAndArgs...)
} }
// Conditionf uses a Comparison to assert a complex condition. // Conditionf uses a Comparison to assert a complex condition.
func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Conditionf(a.t, comp, msg, args...) return Conditionf(a.t, comp, msg, args...)
} }
@ -28,6 +34,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}
// a.Contains(["Hello", "World"], "World") // a.Contains(["Hello", "World"], "World")
// a.Contains({"Hello": "World"}, "Hello") // a.Contains({"Hello": "World"}, "Hello")
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Contains(a.t, s, contains, msgAndArgs...) return Contains(a.t, s, contains, msgAndArgs...)
} }
@ -38,16 +47,25 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ..
// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") // a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") // a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Containsf(a.t, s, contains, msg, args...) return Containsf(a.t, s, contains, msg, args...)
} }
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return DirExists(a.t, path, msgAndArgs...) return DirExists(a.t, path, msgAndArgs...)
} }
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return DirExistsf(a.t, path, msg, args...) return DirExistsf(a.t, path, msg, args...)
} }
@ -57,6 +75,9 @@ func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bo
// //
// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) // a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ElementsMatch(a.t, listA, listB, msgAndArgs...) return ElementsMatch(a.t, listA, listB, msgAndArgs...)
} }
@ -66,6 +87,9 @@ func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndA
// //
// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") // a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ElementsMatchf(a.t, listA, listB, msg, args...) return ElementsMatchf(a.t, listA, listB, msg, args...)
} }
@ -74,6 +98,9 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
// //
// a.Empty(obj) // a.Empty(obj)
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Empty(a.t, object, msgAndArgs...) return Empty(a.t, object, msgAndArgs...)
} }
@ -82,6 +109,9 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
// //
// a.Emptyf(obj, "error message %s", "formatted") // a.Emptyf(obj, "error message %s", "formatted")
func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Emptyf(a.t, object, msg, args...) return Emptyf(a.t, object, msg, args...)
} }
@ -93,6 +123,9 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{})
// referenced values (as opposed to the memory addresses). Function equality // referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail. // cannot be determined and will always fail.
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Equal(a.t, expected, actual, msgAndArgs...) return Equal(a.t, expected, actual, msgAndArgs...)
} }
@ -102,6 +135,9 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
// a.EqualError(err, expectedErrorString) // a.EqualError(err, expectedErrorString)
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EqualError(a.t, theError, errString, msgAndArgs...) return EqualError(a.t, theError, errString, msgAndArgs...)
} }
@ -111,6 +147,9 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") // a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EqualErrorf(a.t, theError, errString, msg, args...) return EqualErrorf(a.t, theError, errString, msg, args...)
} }
@ -119,6 +158,9 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
// //
// a.EqualValues(uint32(123), int32(123)) // a.EqualValues(uint32(123), int32(123))
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EqualValues(a.t, expected, actual, msgAndArgs...) return EqualValues(a.t, expected, actual, msgAndArgs...)
} }
@ -127,6 +169,9 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
// //
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return EqualValuesf(a.t, expected, actual, msg, args...) return EqualValuesf(a.t, expected, actual, msg, args...)
} }
@ -138,6 +183,9 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg
// referenced values (as opposed to the memory addresses). Function equality // referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail. // cannot be determined and will always fail.
func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Equalf(a.t, expected, actual, msg, args...) return Equalf(a.t, expected, actual, msg, args...)
} }
@ -148,6 +196,9 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
// assert.Equal(t, expectedError, err) // assert.Equal(t, expectedError, err)
// } // }
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Error(a.t, err, msgAndArgs...) return Error(a.t, err, msgAndArgs...)
} }
@ -158,6 +209,9 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
// assert.Equal(t, expectedErrorf, err) // assert.Equal(t, expectedErrorf, err)
// } // }
func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Errorf(a.t, err, msg, args...) return Errorf(a.t, err, msg, args...)
} }
@ -165,6 +219,9 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
// //
// a.Exactly(int32(123), int64(123)) // a.Exactly(int32(123), int64(123))
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Exactly(a.t, expected, actual, msgAndArgs...) return Exactly(a.t, expected, actual, msgAndArgs...)
} }
@ -172,26 +229,41 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg
// //
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Exactlyf(a.t, expected, actual, msg, args...) return Exactlyf(a.t, expected, actual, msg, args...)
} }
// Fail reports a failure through // Fail reports a failure through
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Fail(a.t, failureMessage, msgAndArgs...) return Fail(a.t, failureMessage, msgAndArgs...)
} }
// FailNow fails test // FailNow fails test
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return FailNow(a.t, failureMessage, msgAndArgs...) return FailNow(a.t, failureMessage, msgAndArgs...)
} }
// FailNowf fails test // FailNowf fails test
func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return FailNowf(a.t, failureMessage, msg, args...) return FailNowf(a.t, failureMessage, msg, args...)
} }
// Failf reports a failure through // Failf reports a failure through
func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Failf(a.t, failureMessage, msg, args...) return Failf(a.t, failureMessage, msg, args...)
} }
@ -199,6 +271,9 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{
// //
// a.False(myBool) // a.False(myBool)
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return False(a.t, value, msgAndArgs...) return False(a.t, value, msgAndArgs...)
} }
@ -206,56 +281,77 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
// //
// a.Falsef(myBool, "error message %s", "formatted") // a.Falsef(myBool, "error message %s", "formatted")
func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Falsef(a.t, value, msg, args...) return Falsef(a.t, value, msg, args...)
} }
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return FileExists(a.t, path, msgAndArgs...) return FileExists(a.t, path, msgAndArgs...)
} }
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return FileExistsf(a.t, path, msg, args...) return FileExistsf(a.t, path, msg, args...)
} }
// HTTPBodyContains asserts that a specified handler returns a // HTTPBodyContains asserts that a specified handler returns a
// body that contains a string. // body that contains a string.
// //
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
} }
// HTTPBodyContainsf asserts that a specified handler returns a // HTTPBodyContainsf asserts that a specified handler returns a
// body that contains a string. // body that contains a string.
// //
// a.HTTPBodyContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
} }
// HTTPBodyNotContains asserts that a specified handler returns a // HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string. // body that does not contain a string.
// //
// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
} }
// HTTPBodyNotContainsf asserts that a specified handler returns a // HTTPBodyNotContainsf asserts that a specified handler returns a
// body that does not contain a string. // body that does not contain a string.
// //
// a.HTTPBodyNotContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
} }
@ -265,6 +361,9 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPError(a.t, handler, method, url, values, msgAndArgs...) return HTTPError(a.t, handler, method, url, values, msgAndArgs...)
} }
@ -274,6 +373,9 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
// //
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPErrorf(a.t, handler, method, url, values, msg, args...) return HTTPErrorf(a.t, handler, method, url, values, msg, args...)
} }
@ -283,6 +385,9 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
} }
@ -292,6 +397,9 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
// //
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
} }
@ -301,6 +409,9 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
} }
@ -310,6 +421,9 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) return HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
} }
@ -317,6 +431,9 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s
// //
// a.Implements((*MyInterface)(nil), new(MyObject)) // a.Implements((*MyInterface)(nil), new(MyObject))
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Implements(a.t, interfaceObject, object, msgAndArgs...) return Implements(a.t, interfaceObject, object, msgAndArgs...)
} }
@ -324,6 +441,9 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{},
// //
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Implementsf(a.t, interfaceObject, object, msg, args...) return Implementsf(a.t, interfaceObject, object, msg, args...)
} }
@ -331,26 +451,41 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}
// //
// a.InDelta(math.Pi, (22 / 7.0), 0.01) // a.InDelta(math.Pi, (22 / 7.0), 0.01)
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDelta(a.t, expected, actual, delta, msgAndArgs...) return InDelta(a.t, expected, actual, delta, msgAndArgs...)
} }
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
} }
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
} }
// InDeltaSlice is the same as InDelta, except it compares two slices. // InDeltaSlice is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
} }
// InDeltaSlicef is the same as InDelta, except it compares two slices. // InDeltaSlicef is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) return InDeltaSlicef(a.t, expected, actual, delta, msg, args...)
} }
@ -358,36 +493,57 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del
// //
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InDeltaf(a.t, expected, actual, delta, msg, args...) return InDeltaf(a.t, expected, actual, delta, msg, args...)
} }
// InEpsilon asserts that expected and actual have a relative error less than epsilon // InEpsilon asserts that expected and actual have a relative error less than epsilon
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
} }
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...)
} }
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
} }
// InEpsilonf asserts that expected and actual have a relative error less than epsilon // InEpsilonf asserts that expected and actual have a relative error less than epsilon
func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
} }
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsType(a.t, expectedType, object, msgAndArgs...) return IsType(a.t, expectedType, object, msgAndArgs...)
} }
// IsTypef asserts that the specified objects are of the same type. // IsTypef asserts that the specified objects are of the same type.
func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsTypef(a.t, expectedType, object, msg, args...) return IsTypef(a.t, expectedType, object, msg, args...)
} }
@ -395,6 +551,9 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s
// //
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return JSONEq(a.t, expected, actual, msgAndArgs...) return JSONEq(a.t, expected, actual, msgAndArgs...)
} }
@ -402,6 +561,9 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf
// //
// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") // a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return JSONEqf(a.t, expected, actual, msg, args...) return JSONEqf(a.t, expected, actual, msg, args...)
} }
@ -410,6 +572,9 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
// //
// a.Len(mySlice, 3) // a.Len(mySlice, 3)
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Len(a.t, object, length, msgAndArgs...) return Len(a.t, object, length, msgAndArgs...)
} }
@ -418,6 +583,9 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface
// //
// a.Lenf(mySlice, 3, "error message %s", "formatted") // a.Lenf(mySlice, 3, "error message %s", "formatted")
func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Lenf(a.t, object, length, msg, args...) return Lenf(a.t, object, length, msg, args...)
} }
@ -425,6 +593,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in
// //
// a.Nil(err) // a.Nil(err)
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Nil(a.t, object, msgAndArgs...) return Nil(a.t, object, msgAndArgs...)
} }
@ -432,6 +603,9 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
// //
// a.Nilf(err, "error message %s", "formatted") // a.Nilf(err, "error message %s", "formatted")
func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Nilf(a.t, object, msg, args...) return Nilf(a.t, object, msg, args...)
} }
@ -442,6 +616,9 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b
// assert.Equal(t, expectedObj, actualObj) // assert.Equal(t, expectedObj, actualObj)
// } // }
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoError(a.t, err, msgAndArgs...) return NoError(a.t, err, msgAndArgs...)
} }
@ -452,6 +629,9 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
// assert.Equal(t, expectedObj, actualObj) // assert.Equal(t, expectedObj, actualObj)
// } // }
func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoErrorf(a.t, err, msg, args...) return NoErrorf(a.t, err, msg, args...)
} }
@ -462,6 +642,9 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
// a.NotContains(["Hello", "World"], "Earth") // a.NotContains(["Hello", "World"], "Earth")
// a.NotContains({"Hello": "World"}, "Earth") // a.NotContains({"Hello": "World"}, "Earth")
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotContains(a.t, s, contains, msgAndArgs...) return NotContains(a.t, s, contains, msgAndArgs...)
} }
@ -472,6 +655,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs
// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") // a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") // a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotContainsf(a.t, s, contains, msg, args...) return NotContainsf(a.t, s, contains, msg, args...)
} }
@ -482,6 +668,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
// assert.Equal(t, "two", obj[1]) // assert.Equal(t, "two", obj[1])
// } // }
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEmpty(a.t, object, msgAndArgs...) return NotEmpty(a.t, object, msgAndArgs...)
} }
@ -492,6 +681,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo
// assert.Equal(t, "two", obj[1]) // assert.Equal(t, "two", obj[1])
// } // }
func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEmptyf(a.t, object, msg, args...) return NotEmptyf(a.t, object, msg, args...)
} }
@ -502,6 +694,9 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface
// Pointer variable equality is determined based on the equality of the // Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). // referenced values (as opposed to the memory addresses).
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEqual(a.t, expected, actual, msgAndArgs...) return NotEqual(a.t, expected, actual, msgAndArgs...)
} }
@ -512,6 +707,9 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
// Pointer variable equality is determined based on the equality of the // Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). // referenced values (as opposed to the memory addresses).
func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEqualf(a.t, expected, actual, msg, args...) return NotEqualf(a.t, expected, actual, msg, args...)
} }
@ -519,6 +717,9 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
// //
// a.NotNil(err) // a.NotNil(err)
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotNil(a.t, object, msgAndArgs...) return NotNil(a.t, object, msgAndArgs...)
} }
@ -526,6 +727,9 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool
// //
// a.NotNilf(err, "error message %s", "formatted") // a.NotNilf(err, "error message %s", "formatted")
func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotNilf(a.t, object, msg, args...) return NotNilf(a.t, object, msg, args...)
} }
@ -533,6 +737,9 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}
// //
// a.NotPanics(func(){ RemainCalm() }) // a.NotPanics(func(){ RemainCalm() })
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotPanics(a.t, f, msgAndArgs...) return NotPanics(a.t, f, msgAndArgs...)
} }
@ -540,6 +747,9 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool
// //
// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") // a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotPanicsf(a.t, f, msg, args...) return NotPanicsf(a.t, f, msg, args...)
} }
@ -548,6 +758,9 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") // a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
// a.NotRegexp("^start", "it's not starting") // a.NotRegexp("^start", "it's not starting")
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotRegexp(a.t, rx, str, msgAndArgs...) return NotRegexp(a.t, rx, str, msgAndArgs...)
} }
@ -556,6 +769,9 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotRegexpf(a.t, rx, str, msg, args...) return NotRegexpf(a.t, rx, str, msg, args...)
} }
@ -564,6 +780,9 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg
// //
// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") // a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotSubset(a.t, list, subset, msgAndArgs...) return NotSubset(a.t, list, subset, msgAndArgs...)
} }
@ -572,16 +791,25 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
// //
// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") // a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotSubsetf(a.t, list, subset, msg, args...) return NotSubsetf(a.t, list, subset, msg, args...)
} }
// NotZero asserts that i is not the zero value for its type. // NotZero asserts that i is not the zero value for its type.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotZero(a.t, i, msgAndArgs...) return NotZero(a.t, i, msgAndArgs...)
} }
// NotZerof asserts that i is not the zero value for its type. // NotZerof asserts that i is not the zero value for its type.
func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotZerof(a.t, i, msg, args...) return NotZerof(a.t, i, msg, args...)
} }
@ -589,6 +817,9 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo
// //
// a.Panics(func(){ GoCrazy() }) // a.Panics(func(){ GoCrazy() })
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Panics(a.t, f, msgAndArgs...) return Panics(a.t, f, msgAndArgs...)
} }
@ -597,6 +828,9 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
// //
// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) // a.PanicsWithValue("crazy error", func(){ GoCrazy() })
func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return PanicsWithValue(a.t, expected, f, msgAndArgs...) return PanicsWithValue(a.t, expected, f, msgAndArgs...)
} }
@ -605,6 +839,9 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA
// //
// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") // a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return PanicsWithValuef(a.t, expected, f, msg, args...) return PanicsWithValuef(a.t, expected, f, msg, args...)
} }
@ -612,6 +849,9 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg
// //
// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") // a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Panicsf(a.t, f, msg, args...) return Panicsf(a.t, f, msg, args...)
} }
@ -620,6 +860,9 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
// a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp(regexp.MustCompile("start"), "it's starting")
// a.Regexp("start...$", "it's not starting") // a.Regexp("start...$", "it's not starting")
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Regexp(a.t, rx, str, msgAndArgs...) return Regexp(a.t, rx, str, msgAndArgs...)
} }
@ -628,6 +871,9 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Regexpf(a.t, rx, str, msg, args...) return Regexpf(a.t, rx, str, msg, args...)
} }
@ -636,6 +882,9 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args .
// //
// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") // a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Subset(a.t, list, subset, msgAndArgs...) return Subset(a.t, list, subset, msgAndArgs...)
} }
@ -644,6 +893,9 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
// //
// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") // a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Subsetf(a.t, list, subset, msg, args...) return Subsetf(a.t, list, subset, msg, args...)
} }
@ -651,6 +903,9 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a
// //
// a.True(myBool) // a.True(myBool)
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return True(a.t, value, msgAndArgs...) return True(a.t, value, msgAndArgs...)
} }
@ -658,6 +913,9 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
// //
// a.Truef(myBool, "error message %s", "formatted") // a.Truef(myBool, "error message %s", "formatted")
func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Truef(a.t, value, msg, args...) return Truef(a.t, value, msg, args...)
} }
@ -665,6 +923,9 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
// //
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) // a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
} }
@ -672,15 +933,24 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta
// //
// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") // a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return WithinDurationf(a.t, expected, actual, delta, msg, args...) return WithinDurationf(a.t, expected, actual, delta, msg, args...)
} }
// Zero asserts that i is the zero value for its type. // Zero asserts that i is the zero value for its type.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Zero(a.t, i, msgAndArgs...) return Zero(a.t, i, msgAndArgs...)
} }
// Zerof asserts that i is the zero value for its type. // Zerof asserts that i is the zero value for its type.
func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Zerof(a.t, i, msg, args...) return Zerof(a.t, i, msg, args...)
} }

View File

@ -1,4 +1,5 @@
{{.CommentWithoutT "a"}} {{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
if h, ok := a.t.(tHelper); ok { h.Helper() }
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
} }

View File

@ -27,6 +27,22 @@ type TestingT interface {
Errorf(format string, args ...interface{}) Errorf(format string, args ...interface{})
} }
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
// for table driven tests.
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
// for table driven tests.
type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
// for table driven tests.
type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
// Comparison a custom function that returns true on success and false on failure // Comparison a custom function that returns true on success and false on failure
type Comparison func() (success bool) type Comparison func() (success bool)
@ -38,21 +54,23 @@ type Comparison func() (success bool)
// //
// This function does no assertion of any kind. // This function does no assertion of any kind.
func ObjectsAreEqual(expected, actual interface{}) bool { func ObjectsAreEqual(expected, actual interface{}) bool {
if expected == nil || actual == nil { if expected == nil || actual == nil {
return expected == actual return expected == actual
} }
if exp, ok := expected.([]byte); ok {
act, ok := actual.([]byte)
if !ok {
return false
} else if exp == nil || act == nil {
return exp == nil && act == nil
}
return bytes.Equal(exp, act)
}
return reflect.DeepEqual(expected, actual)
exp, ok := expected.([]byte)
if !ok {
return reflect.DeepEqual(expected, actual)
}
act, ok := actual.([]byte)
if !ok {
return false
}
if exp == nil || act == nil {
return exp == nil && act == nil
}
return bytes.Equal(exp, act)
} }
// ObjectsAreEqualValues gets whether two objects are equal, or if their // ObjectsAreEqualValues gets whether two objects are equal, or if their
@ -156,21 +174,6 @@ func isTest(name, prefix string) bool {
return !unicode.IsLower(rune) return !unicode.IsLower(rune)
} }
// getWhitespaceString returns a string that is long enough to overwrite the default
// output from the go testing framework.
func getWhitespaceString() string {
_, file, line, ok := runtime.Caller(1)
if !ok {
return ""
}
parts := strings.Split(file, "/")
file = parts[len(parts)-1]
return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line)))
}
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
if len(msgAndArgs) == 0 || msgAndArgs == nil { if len(msgAndArgs) == 0 || msgAndArgs == nil {
return "" return ""
@ -195,7 +198,7 @@ func indentMessageLines(message string, longestLabelLen int) string {
// no need to align first line because it starts at the correct location (after the label) // no need to align first line because it starts at the correct location (after the label)
if i != 0 { if i != 0 {
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
} }
outBuf.WriteString(scanner.Text()) outBuf.WriteString(scanner.Text())
} }
@ -209,6 +212,9 @@ type failNower interface {
// FailNow fails test // FailNow fails test
func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
Fail(t, failureMessage, msgAndArgs...) Fail(t, failureMessage, msgAndArgs...)
// We cannot extend TestingT with FailNow() and // We cannot extend TestingT with FailNow() and
@ -227,8 +233,11 @@ func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool
// Fail reports a failure through // Fail reports a failure through
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
content := []labeledContent{ content := []labeledContent{
{"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")}, {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")},
{"Error", failureMessage}, {"Error", failureMessage},
} }
@ -244,7 +253,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
content = append(content, labeledContent{"Messages", message}) content = append(content, labeledContent{"Messages", message})
} }
t.Errorf("%s", "\r"+getWhitespaceString()+labeledOutput(content...)) t.Errorf("\n%s", ""+labeledOutput(content...))
return false return false
} }
@ -256,7 +265,7 @@ type labeledContent struct {
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
// //
// \r\t{{label}}:{{align_spaces}}\t{{content}}\n // \t{{label}}:{{align_spaces}}\t{{content}}\n
// //
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
@ -272,7 +281,7 @@ func labeledOutput(content ...labeledContent) string {
} }
var output string var output string
for _, v := range content { for _, v := range content {
output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
} }
return output return output
} }
@ -281,6 +290,9 @@ func labeledOutput(content ...labeledContent) string {
// //
// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) // assert.Implements(t, (*MyInterface)(nil), new(MyObject))
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
interfaceType := reflect.TypeOf(interfaceObject).Elem() interfaceType := reflect.TypeOf(interfaceObject).Elem()
if object == nil { if object == nil {
@ -295,6 +307,9 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
@ -311,6 +326,9 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
// referenced values (as opposed to the memory addresses). Function equality // referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail. // cannot be determined and will always fail.
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err := validateEqualArgs(expected, actual); err != nil { if err := validateEqualArgs(expected, actual); err != nil {
return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
expected, actual, err), msgAndArgs...) expected, actual, err), msgAndArgs...)
@ -349,6 +367,9 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) {
// //
// assert.EqualValues(t, uint32(123), int32(123)) // assert.EqualValues(t, uint32(123), int32(123))
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !ObjectsAreEqualValues(expected, actual) { if !ObjectsAreEqualValues(expected, actual) {
diff := diff(expected, actual) diff := diff(expected, actual)
@ -366,12 +387,15 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
// //
// assert.Exactly(t, int32(123), int64(123)) // assert.Exactly(t, int32(123), int64(123))
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
aType := reflect.TypeOf(expected) aType := reflect.TypeOf(expected)
bType := reflect.TypeOf(actual) bType := reflect.TypeOf(actual)
if aType != bType { if aType != bType {
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...) return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
} }
return Equal(t, expected, actual, msgAndArgs...) return Equal(t, expected, actual, msgAndArgs...)
@ -382,6 +406,9 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
// //
// assert.NotNil(t, err) // assert.NotNil(t, err)
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !isNil(object) { if !isNil(object) {
return true return true
} }
@ -407,6 +434,9 @@ func isNil(object interface{}) bool {
// //
// assert.Nil(t, err) // assert.Nil(t, err)
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if isNil(object) { if isNil(object) {
return true return true
} }
@ -446,6 +476,9 @@ func isEmpty(object interface{}) bool {
// //
// assert.Empty(t, obj) // assert.Empty(t, obj)
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
pass := isEmpty(object) pass := isEmpty(object)
if !pass { if !pass {
@ -463,6 +496,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
// assert.Equal(t, "two", obj[1]) // assert.Equal(t, "two", obj[1])
// } // }
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
pass := !isEmpty(object) pass := !isEmpty(object)
if !pass { if !pass {
@ -490,6 +526,9 @@ func getLen(x interface{}) (ok bool, length int) {
// //
// assert.Len(t, mySlice, 3) // assert.Len(t, mySlice, 3)
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
ok, l := getLen(object) ok, l := getLen(object)
if !ok { if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
@ -505,6 +544,14 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
// //
// assert.True(t, myBool) // assert.True(t, myBool)
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if h, ok := t.(interface {
Helper()
}); ok {
h.Helper()
}
if value != true { if value != true {
return Fail(t, "Should be true", msgAndArgs...) return Fail(t, "Should be true", msgAndArgs...)
@ -518,6 +565,9 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
// //
// assert.False(t, myBool) // assert.False(t, myBool)
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if value != false { if value != false {
return Fail(t, "Should be false", msgAndArgs...) return Fail(t, "Should be false", msgAndArgs...)
@ -534,6 +584,9 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
// Pointer variable equality is determined based on the equality of the // Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). // referenced values (as opposed to the memory addresses).
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err := validateEqualArgs(expected, actual); err != nil { if err := validateEqualArgs(expected, actual); err != nil {
return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
expected, actual, err), msgAndArgs...) expected, actual, err), msgAndArgs...)
@ -592,6 +645,9 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
// assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, ["Hello", "World"], "World")
// assert.Contains(t, {"Hello": "World"}, "Hello") // assert.Contains(t, {"Hello": "World"}, "Hello")
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
ok, found := includeElement(s, contains) ok, found := includeElement(s, contains)
if !ok { if !ok {
@ -612,6 +668,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
// assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, ["Hello", "World"], "Earth")
// assert.NotContains(t, {"Hello": "World"}, "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth")
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
ok, found := includeElement(s, contains) ok, found := includeElement(s, contains)
if !ok { if !ok {
@ -630,6 +689,9 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
// //
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") // assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if subset == nil { if subset == nil {
return true // we consider nil to be equal to the nil set return true // we consider nil to be equal to the nil set
} }
@ -671,6 +733,9 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
// //
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") // assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if subset == nil { if subset == nil {
return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
} }
@ -713,6 +778,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
// //
// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if isEmpty(listA) && isEmpty(listB) { if isEmpty(listA) && isEmpty(listB) {
return true return true
} }
@ -763,6 +831,9 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
// Condition uses a Comparison to assert a complex condition. // Condition uses a Comparison to assert a complex condition.
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
result := comp() result := comp()
if !result { if !result {
Fail(t, "Condition failed!", msgAndArgs...) Fail(t, "Condition failed!", msgAndArgs...)
@ -800,9 +871,12 @@ func didPanic(f PanicTestFunc) (bool, interface{}) {
// //
// assert.Panics(t, func(){ GoCrazy() }) // assert.Panics(t, func(){ GoCrazy() })
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
} }
return true return true
@ -813,13 +887,16 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
// //
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
funcDidPanic, panicValue := didPanic(f) funcDidPanic, panicValue := didPanic(f)
if !funcDidPanic { if !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
} }
if panicValue != expected { if panicValue != expected {
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%v\n\r\tPanic value:\t%v", f, expected, panicValue), msgAndArgs...) return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...)
} }
return true return true
@ -829,9 +906,12 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
// //
// assert.NotPanics(t, func(){ RemainCalm() }) // assert.NotPanics(t, func(){ RemainCalm() })
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if funcDidPanic, panicValue := didPanic(f); funcDidPanic { if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...)
} }
return true return true
@ -841,6 +921,9 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
// //
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
dt := expected.Sub(actual) dt := expected.Sub(actual)
if dt < -delta || dt > delta { if dt < -delta || dt > delta {
@ -890,6 +973,9 @@ func toFloat(x interface{}) (float64, bool) {
// //
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
af, aok := toFloat(expected) af, aok := toFloat(expected)
bf, bok := toFloat(actual) bf, bok := toFloat(actual)
@ -916,6 +1002,9 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
// InDeltaSlice is the same as InDelta, except it compares two slices. // InDeltaSlice is the same as InDelta, except it compares two slices.
func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if expected == nil || actual == nil || if expected == nil || actual == nil ||
reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(actual).Kind() != reflect.Slice ||
reflect.TypeOf(expected).Kind() != reflect.Slice { reflect.TypeOf(expected).Kind() != reflect.Slice {
@ -937,6 +1026,9 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if expected == nil || actual == nil || if expected == nil || actual == nil ||
reflect.TypeOf(actual).Kind() != reflect.Map || reflect.TypeOf(actual).Kind() != reflect.Map ||
reflect.TypeOf(expected).Kind() != reflect.Map { reflect.TypeOf(expected).Kind() != reflect.Map {
@ -994,6 +1086,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
// InEpsilon asserts that expected and actual have a relative error less than epsilon // InEpsilon asserts that expected and actual have a relative error less than epsilon
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
actualEpsilon, err := calcRelativeError(expected, actual) actualEpsilon, err := calcRelativeError(expected, actual)
if err != nil { if err != nil {
return Fail(t, err.Error(), msgAndArgs...) return Fail(t, err.Error(), msgAndArgs...)
@ -1008,6 +1103,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if expected == nil || actual == nil || if expected == nil || actual == nil ||
reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(actual).Kind() != reflect.Slice ||
reflect.TypeOf(expected).Kind() != reflect.Slice { reflect.TypeOf(expected).Kind() != reflect.Slice {
@ -1038,6 +1136,9 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
// assert.Equal(t, expectedObj, actualObj) // assert.Equal(t, expectedObj, actualObj)
// } // }
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err != nil { if err != nil {
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
} }
@ -1052,6 +1153,9 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
// assert.Equal(t, expectedError, err) // assert.Equal(t, expectedError, err)
// } // }
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err == nil { if err == nil {
return Fail(t, "An error is expected but got nil.", msgAndArgs...) return Fail(t, "An error is expected but got nil.", msgAndArgs...)
@ -1066,6 +1170,9 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
// assert.EqualError(t, err, expectedErrorString) // assert.EqualError(t, err, expectedErrorString)
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !Error(t, theError, msgAndArgs...) { if !Error(t, theError, msgAndArgs...) {
return false return false
} }
@ -1099,6 +1206,9 @@ func matchRegexp(rx interface{}, str interface{}) bool {
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
// assert.Regexp(t, "start...$", "it's not starting") // assert.Regexp(t, "start...$", "it's not starting")
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
match := matchRegexp(rx, str) match := matchRegexp(rx, str)
@ -1114,6 +1224,9 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
// assert.NotRegexp(t, "^start", "it's not starting") // assert.NotRegexp(t, "^start", "it's not starting")
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
match := matchRegexp(rx, str) match := matchRegexp(rx, str)
if match { if match {
@ -1126,6 +1239,9 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
// Zero asserts that i is the zero value for its type. // Zero asserts that i is the zero value for its type.
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
} }
@ -1134,6 +1250,9 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
// NotZero asserts that i is not the zero value for its type. // NotZero asserts that i is not the zero value for its type.
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
} }
@ -1142,6 +1261,9 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
info, err := os.Lstat(path) info, err := os.Lstat(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -1157,6 +1279,9 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
info, err := os.Lstat(path) info, err := os.Lstat(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -1174,6 +1299,9 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
// //
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
var expectedJSONAsInterface, actualJSONAsInterface interface{} var expectedJSONAsInterface, actualJSONAsInterface interface{}
if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
@ -1212,12 +1340,18 @@ func diff(expected interface{}, actual interface{}) string {
return "" return ""
} }
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {
return "" return ""
} }
e := spewConfig.Sdump(expected) var e, a string
a := spewConfig.Sdump(actual) if ek != reflect.String {
e = spewConfig.Sdump(expected)
a = spewConfig.Sdump(actual)
} else {
e = expected.(string)
a = actual.(string)
}
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(e), A: difflib.SplitLines(e),
@ -1254,3 +1388,7 @@ var spewConfig = spew.ConfigState{
DisableCapacities: true, DisableCapacities: true,
SortKeys: true, SortKeys: true,
} }
type tHelper interface {
Helper()
}

View File

@ -12,10 +12,11 @@ import (
// an error if building a new request fails. // an error if building a new request fails.
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) req, err := http.NewRequest(method, url, nil)
if err != nil { if err != nil {
return -1, err return -1, err
} }
req.URL.RawQuery = values.Encode()
handler(w, req) handler(w, req)
return w.Code, nil return w.Code, nil
} }
@ -26,6 +27,9 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values) code, err := httpCode(handler, method, url, values)
if err != nil { if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@ -46,6 +50,9 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values) code, err := httpCode(handler, method, url, values)
if err != nil { if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@ -66,6 +73,9 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values) code, err := httpCode(handler, method, url, values)
if err != nil { if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@ -95,10 +105,13 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
// HTTPBodyContains asserts that a specified handler returns a // HTTPBodyContains asserts that a specified handler returns a
// body that contains a string. // body that contains a string.
// //
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
body := HTTPBody(handler, method, url, values) body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str)) contains := strings.Contains(body, fmt.Sprint(str))
@ -112,10 +125,13 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
// HTTPBodyNotContains asserts that a specified handler returns a // HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string. // body that does not contain a string.
// //
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
// //
// Returns whether the assertion was successful (true) or not (false). // Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
body := HTTPBody(handler, method, url, values) body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str)) contains := strings.Contains(body, fmt.Sprint(str))

View File

@ -1,3 +1,7 @@
Copyright (c) 2018 Zachary Yedidia. Modifications to atotto/clipboard.
Original license:
Copyright (c) 2013 Ato Araki. All rights reserved. Copyright (c) 2013 Ato Araki. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -1,3 +1,7 @@
# This is a fork of atotto/clipboard
This fork is used for `zyedidia/micro` and has some modifications, namely: support for the primary clipboard on linux and support for an internal clipboard if the system clipboard is not available.
[![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) [![Build Status](https://drone.io/github.com/atotto/clipboard/status.png)](https://drone.io/github.com/atotto/clipboard/latest) [![Build Status](https://travis-ci.org/atotto/clipboard.svg?branch=master)](https://travis-ci.org/atotto/clipboard) [![Build Status](https://drone.io/github.com/atotto/clipboard/status.png)](https://drone.io/github.com/atotto/clipboard/latest)
[![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard) [![GoDoc](https://godoc.org/github.com/atotto/clipboard?status.svg)](http://godoc.org/github.com/atotto/clipboard)

View File

@ -49,20 +49,91 @@ func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error {
return nil return nil
} }
func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int) error { func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error {
buf := make([]byte, step) buf := make([]byte, step)
for y := dy - 1; y >= 0; y-- { if opaque {
min := y*stride + 0 for y := dy - 1; y >= 0; y-- {
max := y*stride + dx*4 min := y*stride + 0
off := 0 max := y*stride + dx*4
for i := min; i < max; i += 4 { off := 0
buf[off+2] = pix[i+0] for i := min; i < max; i += 4 {
buf[off+1] = pix[i+1] buf[off+2] = pix[i+0]
buf[off+0] = pix[i+2] buf[off+1] = pix[i+1]
off += 3 buf[off+0] = pix[i+2]
off += 3
}
if _, err := w.Write(buf); err != nil {
return err
}
} }
if _, err := w.Write(buf); err != nil { } else {
return err for y := dy - 1; y >= 0; y-- {
min := y*stride + 0
max := y*stride + dx*4
off := 0
for i := min; i < max; i += 4 {
a := uint32(pix[i+3])
if a == 0 {
buf[off+2] = 0
buf[off+1] = 0
buf[off+0] = 0
buf[off+3] = 0
off += 4
continue
} else if a == 0xff {
buf[off+2] = pix[i+0]
buf[off+1] = pix[i+1]
buf[off+0] = pix[i+2]
buf[off+3] = 0xff
off += 4
continue
}
buf[off+2] = uint8(((uint32(pix[i+0]) * 0xffff) / a) >> 8)
buf[off+1] = uint8(((uint32(pix[i+1]) * 0xffff) / a) >> 8)
buf[off+0] = uint8(((uint32(pix[i+2]) * 0xffff) / a) >> 8)
buf[off+3] = uint8(a)
off += 4
}
if _, err := w.Write(buf); err != nil {
return err
}
}
}
return nil
}
func encodeNRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error {
buf := make([]byte, step)
if opaque {
for y := dy - 1; y >= 0; y-- {
min := y*stride + 0
max := y*stride + dx*4
off := 0
for i := min; i < max; i += 4 {
buf[off+2] = pix[i+0]
buf[off+1] = pix[i+1]
buf[off+0] = pix[i+2]
off += 3
}
if _, err := w.Write(buf); err != nil {
return err
}
}
} else {
for y := dy - 1; y >= 0; y-- {
min := y*stride + 0
max := y*stride + dx*4
off := 0
for i := min; i < max; i += 4 {
buf[off+2] = pix[i+0]
buf[off+1] = pix[i+1]
buf[off+0] = pix[i+2]
buf[off+3] = pix[i+3]
off += 4
}
if _, err := w.Write(buf); err != nil {
return err
}
} }
} }
return nil return nil
@ -105,6 +176,7 @@ func Encode(w io.Writer, m image.Image) error {
var step int var step int
var palette []byte var palette []byte
var opaque bool
switch m := m.(type) { switch m := m.(type) {
case *image.Gray: case *image.Gray:
step = (d.X + 3) &^ 3 step = (d.X + 3) &^ 3
@ -134,6 +206,28 @@ func Encode(w io.Writer, m image.Image) error {
h.fileSize += uint32(len(palette)) + h.imageSize h.fileSize += uint32(len(palette)) + h.imageSize
h.pixOffset += uint32(len(palette)) h.pixOffset += uint32(len(palette))
h.bpp = 8 h.bpp = 8
case *image.RGBA:
opaque = m.Opaque()
if opaque {
step = (3*d.X + 3) &^ 3
h.bpp = 24
} else {
step = 4 * d.X
h.bpp = 32
}
h.imageSize = uint32(d.Y * step)
h.fileSize += h.imageSize
case *image.NRGBA:
opaque = m.Opaque()
if opaque {
step = (3*d.X + 3) &^ 3
h.bpp = 24
} else {
step = 4 * d.X
h.bpp = 32
}
h.imageSize = uint32(d.Y * step)
h.fileSize += h.imageSize
default: default:
step = (3*d.X + 3) &^ 3 step = (3*d.X + 3) &^ 3
h.imageSize = uint32(d.Y * step) h.imageSize = uint32(d.Y * step)
@ -160,7 +254,9 @@ func Encode(w io.Writer, m image.Image) error {
case *image.Paletted: case *image.Paletted:
return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
case *image.RGBA: case *image.RGBA:
return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step) return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque)
case *image.NRGBA:
return encodeNRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque)
} }
return encode(w, m, step) return encode(w, m, step)
} }

103
vendor/golang.org/x/net/html/parse.go generated vendored
View File

@ -209,27 +209,6 @@ loop:
p.oe = p.oe[:i+1] p.oe = p.oe[:i+1]
} }
// generateAllImpliedEndTags pops nodes off the stack of open elements as long as
// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rb,
// rp, rt, rtc, span, tbody, td, tfoot, th, thead or tr.
func (p *parser) generateAllImpliedEndTags() {
var i int
for i = len(p.oe) - 1; i >= 0; i-- {
n := p.oe[i]
if n.Type == ElementNode {
switch n.DataAtom {
// TODO: remove this divergence from the HTML5 spec
case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb,
a.Rp, a.Rt, a.Rtc, a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
continue
}
}
break
}
p.oe = p.oe[:i+1]
}
// addChild adds a child node n to the top element, and pushes n onto the stack // addChild adds a child node n to the top element, and pushes n onto the stack
// of open elements if it is an element node. // of open elements if it is an element node.
func (p *parser) addChild(n *Node) { func (p *parser) addChild(n *Node) {
@ -276,7 +255,7 @@ func (p *parser) fosterParent(n *Node) {
} }
} }
if template != nil && (table == nil || j < i) { if template != nil && (table == nil || j > i) {
template.AppendChild(n) template.AppendChild(n)
return return
} }
@ -679,11 +658,16 @@ func inHeadIM(p *parser) bool {
if !p.oe.contains(a.Template) { if !p.oe.contains(a.Template) {
return true return true
} }
p.generateAllImpliedEndTags() // TODO: remove this divergence from the HTML5 spec.
if n := p.oe.top(); n.DataAtom != a.Template { //
return true // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
p.generateImpliedEndTags()
for i := len(p.oe) - 1; i >= 0; i-- {
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template {
p.oe = p.oe[:i]
break
}
} }
p.popUntil(defaultScope, a.Template)
p.clearActiveFormattingElements() p.clearActiveFormattingElements()
p.templateStack.pop() p.templateStack.pop()
p.resetInsertionMode() p.resetInsertionMode()
@ -860,9 +844,13 @@ func inBodyIM(p *parser) bool {
// The newline, if any, will be dealt with by the TextToken case. // The newline, if any, will be dealt with by the TextToken case.
p.framesetOK = false p.framesetOK = false
case a.Form: case a.Form:
if p.oe.contains(a.Template) || p.form == nil { if p.form != nil && !p.oe.contains(a.Template) {
p.popUntil(buttonScope, a.P) // Ignore the token
p.addElement() return true
}
p.popUntil(buttonScope, a.P)
p.addElement()
if !p.oe.contains(a.Template) {
p.form = p.top() p.form = p.top()
} }
case a.Li: case a.Li:
@ -1070,13 +1058,7 @@ func inBodyIM(p *parser) bool {
p.acknowledgeSelfClosingTag() p.acknowledgeSelfClosingTag()
} }
return true return true
case a.Frame: case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
// TODO: remove this divergence from the HTML5 spec.
if p.oe.contains(a.Template) {
p.addElement()
return true
}
case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
// Ignore the token. // Ignore the token.
default: default:
p.reconstructActiveFormattingElements() p.reconstructActiveFormattingElements()
@ -1098,12 +1080,13 @@ func inBodyIM(p *parser) bool {
p.popUntil(defaultScope, p.tok.DataAtom) p.popUntil(defaultScope, p.tok.DataAtom)
case a.Form: case a.Form:
if p.oe.contains(a.Template) { if p.oe.contains(a.Template) {
if !p.oe.contains(a.Form) { i := p.indexOfElementInScope(defaultScope, a.Form)
if i == -1 {
// Ignore the token. // Ignore the token.
return true return true
} }
p.generateImpliedEndTags() p.generateImpliedEndTags()
if p.tok.DataAtom == a.Form { if p.oe[i].DataAtom != a.Form {
// Ignore the token. // Ignore the token.
return true return true
} }
@ -1346,9 +1329,6 @@ func textIM(p *parser) bool {
// Section 12.2.6.4.9. // Section 12.2.6.4.9.
func inTableIM(p *parser) bool { func inTableIM(p *parser) bool {
switch p.tok.Type { switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken: case TextToken:
p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1)
switch p.oe.top().DataAtom { switch p.oe.top().DataAtom {
@ -1443,6 +1423,8 @@ func inTableIM(p *parser) bool {
case DoctypeToken: case DoctypeToken:
// Ignore the token. // Ignore the token.
return true return true
case ErrorToken:
return inBodyIM(p)
} }
p.fosterParenting = true p.fosterParenting = true
@ -1545,6 +1527,8 @@ func inColumnGroupIM(p *parser) bool {
case a.Template: case a.Template:
return inHeadIM(p) return inHeadIM(p)
} }
case ErrorToken:
return inBodyIM(p)
} }
if p.oe.top().DataAtom != a.Colgroup { if p.oe.top().DataAtom != a.Colgroup {
return true return true
@ -1709,9 +1693,6 @@ func inCellIM(p *parser) bool {
// Section 12.2.6.4.16. // Section 12.2.6.4.16.
func inSelectIM(p *parser) bool { func inSelectIM(p *parser) bool {
switch p.tok.Type { switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken: case TextToken:
p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) p.addText(strings.Replace(p.tok.Data, "\x00", "", -1))
case StartTagToken: case StartTagToken:
@ -1775,6 +1756,8 @@ func inSelectIM(p *parser) bool {
case DoctypeToken: case DoctypeToken:
// Ignore the token. // Ignore the token.
return true return true
case ErrorToken:
return inBodyIM(p)
} }
return true return true
@ -1841,15 +1824,26 @@ func inTemplateIM(p *parser) bool {
// Ignore the token. // Ignore the token.
return true return true
} }
case ErrorToken:
if !p.oe.contains(a.Template) {
// Ignore the token.
return true
}
// TODO: remove this divergence from the HTML5 spec.
//
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
p.generateImpliedEndTags()
for i := len(p.oe) - 1; i >= 0; i-- {
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template {
p.oe = p.oe[:i]
break
}
}
p.clearActiveFormattingElements()
p.templateStack.pop()
p.resetInsertionMode()
return false
} }
if !p.oe.contains(a.Template) {
// Ignore the token.
return true
}
p.popUntil(defaultScope, a.Template)
p.clearActiveFormattingElements()
p.templateStack.pop()
p.resetInsertionMode()
return false return false
} }
@ -1923,11 +1917,6 @@ func inFramesetIM(p *parser) bool {
p.acknowledgeSelfClosingTag() p.acknowledgeSelfClosingTag()
case a.Noframes: case a.Noframes:
return inHeadIM(p) return inHeadIM(p)
case a.Template:
// TODO: remove this divergence from the HTML5 spec.
//
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
return inTemplateIM(p)
} }
case EndTagToken: case EndTagToken:
switch p.tok.DataAtom { switch p.tok.DataAtom {

5
vendor/gopkg.in/toast.v1/toast.go generated vendored
View File

@ -11,6 +11,7 @@ import (
"text/template" "text/template"
"github.com/nu7hatch/gouuid" "github.com/nu7hatch/gouuid"
"syscall"
) )
var toastTemplate *template.Template var toastTemplate *template.Template
@ -347,7 +348,9 @@ func invokeTemporaryScript(content string) error {
if err != nil { if err != nil {
return err return err
} }
if err = exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file).Run(); err != nil { cmd := exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
if err = cmd.Run(); err != nil {
return err return err
} }
return nil return nil

View File

@ -6,13 +6,16 @@ package gomatrix
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"maunium.net/go/maulogger"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
) )
@ -26,6 +29,7 @@ type Client struct {
Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. Client *http.Client // The underlying HTTP client which will be used to make HTTP requests.
Syncer Syncer // The thing which can process /sync responses Syncer Syncer // The thing which can process /sync responses
Store Storer // The thing which can store rooms/tokens/ids Store Storer // The thing which can store rooms/tokens/ids
Logger maulogger.Logger
// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty, // The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
// no user_id parameter will be sent. // no user_id parameter will be sent.
@ -39,6 +43,7 @@ type Client struct {
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error. // HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
type HTTPError struct { type HTTPError struct {
WrappedError error WrappedError error
RespError *RespError
Message string Message string
Code int Code int
} }
@ -177,6 +182,14 @@ func (cli *Client) StopSync() {
cli.incrementSyncingID() cli.incrementSyncingID()
} }
func (cli *Client) LogRequest(req *http.Request, body string) {
if cli.Logger == nil {
return
}
cli.Logger.Debugfln("%s %s %s", req.Method, req.URL.Path, body)
}
// MakeRequest makes a JSON HTTP request to the given URL. // MakeRequest makes a JSON HTTP request to the given URL.
// If "resBody" is not nil, the response body will be json.Unmarshalled into it. // If "resBody" is not nil, the response body will be json.Unmarshalled into it.
// //
@ -186,12 +199,14 @@ func (cli *Client) StopSync() {
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) { func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
var req *http.Request var req *http.Request
var err error var err error
logBody := "{}"
if reqBody != nil { if reqBody != nil {
var jsonStr []byte var jsonStr []byte
jsonStr, err = json.Marshal(reqBody) jsonStr, err = json.Marshal(reqBody)
if err != nil { if err != nil {
return nil, err return nil, err
} }
logBody = string(jsonStr)
req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr)) req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
} else { } else {
req, err = http.NewRequest(method, httpURL, nil) req, err = http.NewRequest(method, httpURL, nil)
@ -201,6 +216,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
cli.LogRequest(req, logBody)
res, err := cli.Client.Do(req) res, err := cli.Client.Do(req)
if res != nil { if res != nil {
defer res.Body.Close() defer res.Body.Close()
@ -211,9 +227,11 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
contents, err := ioutil.ReadAll(res.Body) contents, err := ioutil.ReadAll(res.Body)
if res.StatusCode/100 != 2 { // not 2xx if res.StatusCode/100 != 2 { // not 2xx
var wrap error var wrap error
var respErr RespError respErr := &RespError{}
if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { if _ = json.Unmarshal(contents, respErr); respErr.ErrCode != "" {
wrap = respErr wrap = respErr
} else {
respErr = nil
} }
// If we failed to decode as RespError, don't just drop the HTTP body, include it in the // If we failed to decode as RespError, don't just drop the HTTP body, include it in the
@ -227,6 +245,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
Code: res.StatusCode, Code: res.StatusCode,
Message: msg, Message: msg,
WrappedError: wrap, WrappedError: wrap,
RespError: respErr,
} }
} }
if err != nil { if err != nil {
@ -342,7 +361,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
} }
} }
if res == nil { if res == nil {
return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?") return nil, fmt.Errorf("registration failed: does this server support m.login.dummy? ")
} }
return res, nil return res, nil
} }
@ -442,17 +461,38 @@ func (cli *Client) SetAvatarURL(url string) (err error) {
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid // SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { func (cli *Client) SendMessageEvent(roomID string, eventType EventType, contentJSON interface{}) (resp *RespSendEvent, err error) {
txnID := txnID() txnID := txnID()
urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID) urlPath := cli.BuildURL("rooms", roomID, "send", eventType.String(), txnID)
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendMassagedMessageEvent(roomID string, eventType EventType, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) {
txnID := txnID()
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "send", eventType.String(), txnID}, map[string]string{
"ts": strconv.FormatInt(ts, 10),
})
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return return
} }
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey // SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { func (cli *Client) SendStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) urlPath := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey)
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendMassagedStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) {
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "state", eventType.String(), stateKey}, map[string]string{
"ts": strconv.FormatInt(ts, 10),
})
_, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return return
} }
@ -460,37 +500,39 @@ func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSO
// SendText sends an m.room.message event into the given room with a msgtype of m.text // SendText sends an m.room.message event into the given room with a msgtype of m.text
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
TextMessage{"m.text", text}) MsgType: MsgText,
Body: text,
})
} }
// SendImage sends an m.room.message event into the given room with a msgtype of m.image // SendImage sends an m.room.message event into the given room with a msgtype of m.image
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
ImageMessage{ MsgType: MsgImage,
MsgType: "m.image", Body: body,
Body: body, URL: url,
URL: url, })
})
} }
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video // SendVideo sends an m.room.message event into the given room with a msgtype of m.video
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
VideoMessage{ MsgType: MsgVideo,
MsgType: "m.video", Body: body,
Body: body, URL: url,
URL: url, })
})
} }
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice // SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
TextMessage{"m.notice", text}) MsgType: MsgNotice,
Body: text,
})
} }
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid // RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
@ -569,11 +611,18 @@ func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *
return return
} }
func (cli *Client) SetPresence(status string) (err error) {
req := ReqPresence{Presence: status}
u := cli.BuildURL("presence", cli.UserID, "status")
_, err = cli.MakeRequest("PUT", u, req, nil)
return
}
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with // StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
// the HTTP response body, or return an error. // the HTTP response body, or return an error.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) { func (cli *Client) StateEvent(roomID string, eventType EventType, stateKey string, outContent interface{}) (err error) {
u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) u := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey)
_, err = cli.MakeRequest("GET", u, nil, outContent) _, err = cli.MakeRequest("GET", u, nil, outContent)
return return
} }
@ -587,18 +636,48 @@ func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength) return cli.Upload(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
}
func (cli *Client) Download(mxcURL string) (io.ReadCloser, error) {
if !strings.HasPrefix(mxcURL, "mxc://") {
return nil, errors.New("invalid Matrix content URL")
}
parts := strings.Split(mxcURL[len("mxc://"):], "/")
if len(parts) != 2 {
return nil, errors.New("invalid Matrix content URL")
}
u := cli.BuildBaseURL("_matrix/media/r0/download", parts[0], parts[1])
resp, err := cli.Client.Get(u)
if err != nil {
return nil, err
}
return resp.Body, nil
}
func (cli *Client) DownloadBytes(mxcURL string) ([]byte, error) {
resp, err := cli.Download(mxcURL)
if err != nil {
return nil, err
}
defer resp.Close()
return ioutil.ReadAll(resp)
}
func (cli *Client) UploadBytes(data []byte, contentType string) (*RespMediaUpload, error) {
return cli.Upload(bytes.NewReader(data), contentType, int64(len(data)))
} }
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI. // UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload // See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { func (cli *Client) Upload(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content) req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", contentType) req.Header.Set("Content-Type", contentType)
req.ContentLength = contentLength req.ContentLength = contentLength
cli.LogRequest(req, fmt.Sprintf("%d bytes", contentLength))
res, err := cli.Client.Do(req) res, err := cli.Client.Do(req)
if res != nil { if res != nil {
defer res.Body.Close() defer res.Body.Close()
@ -666,6 +745,18 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp
return return
} }
func (cli *Client) GetEvent(roomID, eventID string) (resp *Event, err error) {
urlPath := cli.BuildURL("rooms", roomID, "event", eventID)
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
func (cli *Client) MarkRead(roomID, eventID string) (err error) {
urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID)
_, err = cli.MakeRequest("POST", urlPath, struct{}{}, nil)
return
}
// TurnServer returns turn server details and credentials for the client to use when initiating calls. // TurnServer returns turn server details and credentials for the client to use when initiating calls.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) { func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {

View File

@ -1,110 +1,413 @@
package gomatrix package gomatrix
import ( import (
"html" "encoding/json"
"regexp" "strings"
"sync"
)
type EventTypeClass int
const (
// Normal message events
MessageEventType EventTypeClass = iota
// State events
StateEventType
// Ephemeral events
EphemeralEventType
// Account data events
AccountDataEventType
// Unknown events
UnknownEventType
)
type EventType struct {
Type string
Class EventTypeClass
}
func NewEventType(name string) EventType {
evtType := EventType{Type: name}
evtType.Class = evtType.GuessClass()
return evtType
}
func (et *EventType) IsState() bool {
return et.Class == StateEventType
}
func (et *EventType) IsEphemeral() bool {
return et.Class == EphemeralEventType
}
func (et *EventType) IsCustom() bool {
return !strings.HasPrefix(et.Type, "m.")
}
func (et *EventType) GuessClass() EventTypeClass {
switch et.Type {
case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type,
StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateTopic.Type, StatePinnedEvents.Type:
return StateEventType
case EphemeralEventReceipt.Type, EphemeralEventTyping.Type:
return EphemeralEventType
case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type:
return AccountDataEventType
case EventRedaction.Type, EventMessage.Type, EventSticker.Type:
return MessageEventType
default:
return UnknownEventType
}
}
func (et *EventType) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &et.Type)
if err != nil {
return err
}
et.Class = et.GuessClass()
return nil
}
func (et *EventType) MarshalJSON() ([]byte, error) {
return json.Marshal(&et.Type)
}
func (et *EventType) String() string {
return et.Type
}
// State events
var (
StateAliases = EventType{"m.room.aliases", StateEventType}
StateCanonicalAlias = EventType{"m.room.canonical_alias", StateEventType}
StateCreate = EventType{"m.room.create", StateEventType}
StateJoinRules = EventType{"m.room.join_rules", StateEventType}
StateMember = EventType{"m.room.member", StateEventType}
StatePowerLevels = EventType{"m.room.power_levels", StateEventType}
StateRoomName = EventType{"m.room.name", StateEventType}
StateTopic = EventType{"m.room.topic", StateEventType}
StateRoomAvatar = EventType{"m.room.avatar", StateEventType}
StatePinnedEvents = EventType{"m.room.pinned_events", StateEventType}
)
// Message events
var (
EventRedaction = EventType{"m.room.redaction", MessageEventType}
EventMessage = EventType{"m.room.message", MessageEventType}
EventSticker = EventType{"m.sticker", MessageEventType}
)
// Ephemeral events
var (
EphemeralEventReceipt = EventType{"m.receipt", EphemeralEventType}
EphemeralEventTyping = EventType{"m.receipt", EphemeralEventType}
)
// Account data events
var (
AccountDataDirectChats = EventType{"m.direct", AccountDataEventType}
AccountDataPushRules = EventType{"m.push_rules", AccountDataEventType}
AccountDataRoomTags = EventType{"m.tag", AccountDataEventType}
)
type MessageType string
// Msgtypes
const (
MsgText MessageType = "m.text"
MsgEmote = "m.emote"
MsgNotice = "m.notice"
MsgImage = "m.image"
MsgLocation = "m.location"
MsgVideo = "m.video"
MsgAudio = "m.audio"
MsgFile = "m.file"
)
type Format string
// Message formats
const (
FormatHTML Format = "org.matrix.custom.html"
) )
// Event represents a single Matrix event. // Event represents a single Matrix event.
type Event struct { type Event struct {
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
Sender string `json:"sender"` // The user ID of the sender of the event Sender string `json:"sender"` // The user ID of the sender of the event
Type string `json:"type"` // The event type Type EventType `json:"type"` // The event type
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
ID string `json:"event_id"` // The unique ID of this event ID string `json:"event_id"` // The unique ID of this event
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Content map[string]interface{} `json:"content"` // The JSON content of the event. Content Content `json:"content"` // The JSON content of the event.
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
InviteRoomState []StrippedState `json:"invite_room_state"`
}
func (evt *Event) GetStateKey() string {
if evt.StateKey != nil {
return *evt.StateKey
}
return ""
}
type StrippedState struct {
Content Content `json:"content"`
Type EventType `json:"type"`
StateKey string `json:"state_key"`
} }
type Unsigned struct { type Unsigned struct {
PrevContent map[string]interface{} `json:"prev_content,omitempty"` PrevContent *Content `json:"prev_content,omitempty"`
PrevSender string `json:"prev_sender,omitempty"` PrevSender string `json:"prev_sender,omitempty"`
ReplacesState string `json:"replaces_state,omitempty"` ReplacesState string `json:"replaces_state,omitempty"`
Age int64 `json:"age"` Age int64 `json:"age,omitempty"`
} }
// Body returns the value of the "body" key in the event content if it is type Content struct {
// present and is a string. VeryRaw json.RawMessage `json:"-"`
func (event *Event) Body() (body string, ok bool) { Raw map[string]interface{} `json:"-"`
value, exists := event.Content["body"]
if !exists { MsgType MessageType `json:"msgtype,omitempty"`
return Body string `json:"body,omitempty"`
Format Format `json:"format,omitempty"`
FormattedBody string `json:"formatted_body,omitempty"`
Info *FileInfo `json:"info,omitempty"`
URL string `json:"url,omitempty"`
// Membership key for easy access in m.room.member events
Membership Membership `json:"membership,omitempty"`
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
PowerLevels
Member
Aliases []string `json:"aliases,omitempty"`
CanonicalAlias
RoomName
RoomTopic
RoomTags Tags `json:"tags,omitempty"`
TypingUserIDs []string `json:"user_ids,omitempty"`
}
type serializableContent Content
func (content *Content) UnmarshalJSON(data []byte) error {
content.VeryRaw = data
if err := json.Unmarshal(data, &content.Raw); err != nil {
return err
} }
body, ok = value.(string) return json.Unmarshal(data, (*serializableContent)(content))
}
func (content *Content) UnmarshalPowerLevels() (pl PowerLevels, err error) {
err = json.Unmarshal(content.VeryRaw, &pl)
return return
} }
// MessageType returns the value of the "msgtype" key in the event content if func (content *Content) UnmarshalMember() (m Member, err error) {
// it is present and is a string. err = json.Unmarshal(content.VeryRaw, &m)
func (event *Event) MessageType() (msgtype string, ok bool) {
value, exists := event.Content["msgtype"]
if !exists {
return
}
msgtype, ok = value.(string)
return return
} }
// TextMessage is the contents of a Matrix formated message event. func (content *Content) UnmarshalCanonicalAlias() (ca CanonicalAlias, err error) {
type TextMessage struct { err = json.Unmarshal(content.VeryRaw, &ca)
MsgType string `json:"msgtype"` return
Body string `json:"body"`
} }
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image func (content *Content) GetInfo() *FileInfo {
type ImageInfo struct { if content.Info == nil {
Height uint `json:"h,omitempty"` content.Info = &FileInfo{}
Width uint `json:"w,omitempty"` }
Mimetype string `json:"mimetype,omitempty"` return content.Info
Size uint `json:"size,omitempty"`
} }
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video type Tags map[string]struct {
type VideoInfo struct { Order string `json:"order"`
Mimetype string `json:"mimetype,omitempty"` }
ThumbnailInfo ImageInfo `json:"thumbnail_info"`
type RoomName struct {
Name string `json:"name,omitempty"`
}
type RoomTopic struct {
Topic string `json:"topic,omitempty"`
}
// Membership is an enum specifying the membership state of a room member.
type Membership string
// The allowed membership states as specified in spec section 10.5.5.
const (
MembershipJoin Membership = "join"
MembershipLeave Membership = "leave"
MembershipInvite Membership = "invite"
MembershipBan Membership = "ban"
MembershipKnock Membership = "knock"
)
type Member struct {
Membership Membership `json:"membership,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Displayname string `json:"displayname,omitempty"`
ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"`
Reason string `json:"reason,omitempty"`
}
type ThirdPartyInvite struct {
DisplayName string `json:"display_name"`
Signed struct {
Token string `json:"token"`
Signatures json.RawMessage `json:"signatures"`
MXID string `json:"mxid"`
}
}
type CanonicalAlias struct {
Alias string `json:"alias,omitempty"`
}
type PowerLevels struct {
usersLock sync.RWMutex `json:"-"`
Users map[string]int `json:"users,omitempty"`
UsersDefault int `json:"users_default,omitempty"`
eventsLock sync.RWMutex `json:"-"`
Events map[string]int `json:"events,omitempty"`
EventsDefault int `json:"events_default,omitempty"`
StateDefaultPtr *int `json:"state_default,omitempty"`
InvitePtr *int `json:"invite,omitempty"`
KickPtr *int `json:"kick,omitempty"`
BanPtr *int `json:"ban,omitempty"`
RedactPtr *int `json:"redact,omitempty"`
}
func (pl *PowerLevels) Invite() int {
if pl.InvitePtr != nil {
return *pl.InvitePtr
}
return 50
}
func (pl *PowerLevels) Kick() int {
if pl.KickPtr != nil {
return *pl.KickPtr
}
return 50
}
func (pl *PowerLevels) Ban() int {
if pl.BanPtr != nil {
return *pl.BanPtr
}
return 50
}
func (pl *PowerLevels) Redact() int {
if pl.RedactPtr != nil {
return *pl.RedactPtr
}
return 50
}
func (pl *PowerLevels) StateDefault() int {
if pl.StateDefaultPtr != nil {
return *pl.StateDefaultPtr
}
return 50
}
func (pl *PowerLevels) GetUserLevel(userID string) int {
pl.usersLock.RLock()
defer pl.usersLock.RUnlock()
level, ok := pl.Users[userID]
if !ok {
return pl.UsersDefault
}
return level
}
func (pl *PowerLevels) SetUserLevel(userID string, level int) {
pl.usersLock.Lock()
defer pl.usersLock.Unlock()
if level == pl.UsersDefault {
delete(pl.Users, userID)
} else {
pl.Users[userID] = level
}
}
func (pl *PowerLevels) EnsureUserLevel(userID string, level int) bool {
existingLevel := pl.GetUserLevel(userID)
if existingLevel != level {
pl.SetUserLevel(userID, level)
return true
}
return false
}
func (pl *PowerLevels) GetEventLevel(eventType EventType) int {
pl.eventsLock.RLock()
defer pl.eventsLock.RUnlock()
level, ok := pl.Events[eventType.String()]
if !ok {
if eventType.IsState() {
return pl.StateDefault()
}
return pl.EventsDefault
}
return level
}
func (pl *PowerLevels) SetEventLevel(eventType EventType, level int) {
pl.eventsLock.Lock()
defer pl.eventsLock.Unlock()
if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) {
delete(pl.Events, eventType.String())
} else {
pl.Events[eventType.String()] = level
}
}
func (pl *PowerLevels) EnsureEventLevel(eventType EventType, level int) bool {
existingLevel := pl.GetEventLevel(eventType)
if existingLevel != level {
pl.SetEventLevel(eventType, level)
return true
}
return false
}
type FileInfo struct {
MimeType string `json:"mimetype,omitempty"`
ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"`
ThumbnailURL string `json:"thumbnail_url,omitempty"` ThumbnailURL string `json:"thumbnail_url,omitempty"`
Height uint `json:"h,omitempty"` Height int `json:"h,omitempty"`
Width uint `json:"w,omitempty"` Width int `json:"w,omitempty"`
Duration uint `json:"duration,omitempty"` Duration uint `json:"duration,omitempty"`
Size uint `json:"size,omitempty"` Size int `json:"size,omitempty"`
} }
// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo {
type VideoMessage struct { if fileInfo.ThumbnailInfo == nil {
MsgType string `json:"msgtype"` fileInfo.ThumbnailInfo = &FileInfo{}
Body string `json:"body"`
URL string `json:"url"`
Info VideoInfo `json:"info"`
}
// ImageMessage is an m.image event
type ImageMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info ImageInfo `json:"info"`
}
// An HTMLMessage is the contents of a Matrix HTML formated message event.
type HTMLMessage struct {
Body string `json:"body"`
MsgType string `json:"msgtype"`
Format string `json:"format"`
FormattedBody string `json:"formatted_body"`
}
var htmlRegex = regexp.MustCompile("<[^<]+?>")
// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
// to the provided HTML.
func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
return HTMLMessage{
Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
MsgType: msgtype,
Format: "org.matrix.custom.html",
FormattedBody: htmlText,
} }
return fileInfo.ThumbnailInfo
}
type RelatesTo struct {
InReplyTo InReplyTo `json:"m.in_reply_to,omitempty"`
}
type InReplyTo struct {
EventID string `json:"event_id,omitempty"`
// Not required, just for future-proofing
RoomID string `json:"room_id,omitempty"`
} }

96
vendor/maunium.net/go/gomatrix/reply.go generated vendored Normal file
View File

@ -0,0 +1,96 @@
package gomatrix
import (
"fmt"
"regexp"
"strings"
"golang.org/x/net/html"
)
var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`)
func TrimReplyFallbackHTML(html string) string {
return HTMLReplyFallbackRegex.ReplaceAllString(html, "")
}
func TrimReplyFallbackText(text string) string {
if !strings.HasPrefix(text, "> ") || !strings.Contains(text, "\n") {
return text
}
lines := strings.Split(text, "\n")
for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") {
lines = lines[1:]
}
return strings.TrimSpace(strings.Join(lines, "\n"))
}
func (content *Content) RemoveReplyFallback() {
if len(content.GetReplyTo()) > 0 {
if content.Format == FormatHTML {
content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody)
}
content.Body = TrimReplyFallbackText(content.Body)
}
}
func (content *Content) GetReplyTo() string {
if content.RelatesTo != nil {
return content.RelatesTo.InReplyTo.EventID
}
return ""
}
const ReplyFormat = `<mx-reply><blockquote>
<a href="https://matrix.to/#/%s/%s">In reply to</a>
<a href="https://matrix.to/#/%s">%s</a>
%s
</blockquote></mx-reply>
`
func (evt *Event) GenerateReplyFallbackHTML() string {
body := evt.Content.FormattedBody
if len(body) == 0 {
body = html.EscapeString(evt.Content.Body)
}
senderDisplayName := evt.Sender
return fmt.Sprintf(ReplyFormat, evt.RoomID, evt.ID, evt.Sender, senderDisplayName, body)
}
func (evt *Event) GenerateReplyFallbackText() string {
body := evt.Content.Body
lines := strings.Split(strings.TrimSpace(body), "\n")
firstLine, lines := lines[0], lines[1:]
senderDisplayName := evt.Sender
var fallbackText strings.Builder
fmt.Fprintf(&fallbackText, "> <%s> %s", senderDisplayName, firstLine)
for _, line := range lines {
fmt.Fprintf(&fallbackText, "\n> %s", line)
}
fallbackText.WriteString("\n\n")
return fallbackText.String()
}
func (content *Content) SetReply(inReplyTo *Event) {
if content.RelatesTo == nil {
content.RelatesTo = &RelatesTo{}
}
content.RelatesTo.InReplyTo = InReplyTo{
EventID: inReplyTo.ID,
RoomID: inReplyTo.RoomID,
}
if content.MsgType == MsgText || content.MsgType == MsgNotice {
if len(content.FormattedBody) == 0 || content.Format != FormatHTML {
content.FormattedBody = html.EscapeString(content.Body)
content.Format = FormatHTML
}
content.FormattedBody = inReplyTo.GenerateReplyFallbackHTML() + content.FormattedBody
content.Body = inReplyTo.GenerateReplyFallbackText() + content.Body
}
}

View File

@ -31,7 +31,7 @@ type ReqCreateRoom struct {
Invite []string `json:"invite,omitempty"` Invite []string `json:"invite,omitempty"`
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
CreationContent map[string]interface{} `json:"creation_content,omitempty"` CreationContent map[string]interface{} `json:"creation_content,omitempty"`
InitialState []Event `json:"initial_state,omitempty"` InitialState []*Event `json:"initial_state,omitempty"`
Preset string `json:"preset,omitempty"` Preset string `json:"preset,omitempty"`
IsDirect bool `json:"is_direct,omitempty"` IsDirect bool `json:"is_direct,omitempty"`
} }
@ -74,5 +74,9 @@ type ReqUnbanUser struct {
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid // ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
type ReqTyping struct { type ReqTyping struct {
Typing bool `json:"typing"` Typing bool `json:"typing"`
Timeout int64 `json:"timeout"` Timeout int64 `json:"timeout,omitempty"`
}
type ReqPresence struct {
Presence string `json:"presence"`
} }

View File

@ -64,7 +64,7 @@ type RespJoinedMembers struct {
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages // RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
type RespMessages struct { type RespMessages struct {
Start string `json:"start"` Start string `json:"start"`
Chunk []Event `json:"chunk"` Chunk []*Event `json:"chunk"`
End string `json:"end"` End string `json:"end"`
} }

View File

@ -3,7 +3,7 @@ package gomatrix
// Room represents a single Matrix room. // Room represents a single Matrix room.
type Room struct { type Room struct {
ID string ID string
State map[string]map[string]*Event State map[EventType]map[string]*Event
} }
// UpdateState updates the room's current state with the given Event. This will clobber events based // UpdateState updates the room's current state with the given Event. This will clobber events based
@ -17,7 +17,7 @@ func (room Room) UpdateState(event *Event) {
} }
// GetStateEvent returns the state event for the given type/state_key combo, or nil. // GetStateEvent returns the state event for the given type/state_key combo, or nil.
func (room Room) GetStateEvent(eventType string, stateKey string) *Event { func (room Room) GetStateEvent(eventType EventType, stateKey string) *Event {
stateEventMap, _ := room.State[eventType] stateEventMap, _ := room.State[eventType]
event, _ := stateEventMap[stateKey] event, _ := stateEventMap[stateKey]
return event return event
@ -25,17 +25,11 @@ func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
// GetMembershipState returns the membership state of the given user ID in this room. If there is // GetMembershipState returns the membership state of the given user ID in this room. If there is
// no entry for this member, 'leave' is returned for consistency with left users. // no entry for this member, 'leave' is returned for consistency with left users.
func (room Room) GetMembershipState(userID string) string { func (room Room) GetMembershipState(userID string) Membership {
state := "leave" state := MembershipLeave
event := room.GetStateEvent("m.room.member", userID) event := room.GetStateEvent(StateMember, userID)
if event != nil { if event != nil {
membershipState, found := event.Content["membership"] state = event.Content.Membership
if found {
mState, isString := membershipState.(string)
if isString {
state = mState
}
}
} }
return state return state
} }
@ -45,6 +39,6 @@ func NewRoom(roomID string) *Room {
// Init the State map and return a pointer to the Room // Init the State map and return a pointer to the Room
return &Room{ return &Room{
ID: roomID, ID: roomID,
State: make(map[string]map[string]*Event), State: make(map[EventType]map[string]*Event),
} }
} }

View File

@ -25,7 +25,7 @@ type Syncer interface {
type DefaultSyncer struct { type DefaultSyncer struct {
UserID string UserID string
Store Storer Store Storer
listeners map[string][]OnEventListener // event type to listeners array listeners map[EventType][]OnEventListener // event type to listeners array
} }
// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events. // OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
@ -36,7 +36,7 @@ func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
return &DefaultSyncer{ return &DefaultSyncer{
UserID: userID, UserID: userID,
Store: store, Store: store,
listeners: make(map[string][]OnEventListener), listeners: make(map[EventType][]OnEventListener),
} }
} }
@ -88,7 +88,7 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error)
// OnEventType allows callers to be notified when there are new events for the given event type. // OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks. // There are no duplicate checks.
func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) { func (s *DefaultSyncer) OnEventType(eventType EventType, callback OnEventListener) {
_, exists := s.listeners[eventType] _, exists := s.listeners[eventType]
if !exists { if !exists {
s.listeners[eventType] = []OnEventListener{} s.listeners[eventType] = []OnEventListener{}
@ -112,13 +112,8 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool
for roomID, roomData := range resp.Rooms.Join { for roomID, roomData := range resp.Rooms.Join {
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
e := roomData.Timeline.Events[i] e := roomData.Timeline.Events[i]
if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { if e.Type == StateMember && e.GetStateKey() == s.UserID {
m := e.Content["membership"] if e.Content.Membership == "join" {
mship, ok := m.(string)
if !ok {
continue
}
if mship == "join" {
_, ok := resp.Rooms.Join[roomID] _, ok := resp.Rooms.Join[roomID]
if !ok { if !ok {
continue continue

21
vendor/maunium.net/go/maulogger/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Tulir Asokan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
vendor/maunium.net/go/maulogger/README.md generated vendored Normal file
View File

@ -0,0 +1,6 @@
# maulogger
A logger in Go.
Docs: [godoc.org/maunium.net/go/maulogger](https://godoc.org/maunium.net/go/maulogger)
Go get: `go get maunium.net/go/maulogger`

219
vendor/maunium.net/go/maulogger/logger.go generated vendored Normal file
View File

@ -0,0 +1,219 @@
package maulog
import (
"bufio"
"fmt"
"os"
"time"
)
// Level is the severity level of a log entry.
type Level struct {
Name string
Severity, Color int
}
// LogWriter writes to the log with an optional prefix
type LogWriter struct {
Level Level
Prefix string
}
func (lw LogWriter) Write(p []byte) (n int, err error) {
log(lw.Level, fmt.Sprint(lw.Prefix, string(p)))
return len(p), nil
}
// GetColor gets the ANSI escape color code for the log level.
func (lvl Level) GetColor() []byte {
if lvl.Color < 0 {
return []byte("")
}
return []byte(fmt.Sprintf("\x1b[%dm", lvl.Color))
}
// GetReset gets the ANSI escape reset code.
func (lvl Level) GetReset() []byte {
if lvl.Color < 0 {
return []byte("")
}
return []byte("\x1b[0m")
}
var (
// Debug is the level for debug messages.
Debug = Level{Name: "DEBUG", Color: 36, Severity: 0}
// Info is the level for basic log messages.
Info = Level{Name: "INFO", Color: -1, Severity: 10}
// Warn is the level saying that something went wrong, but the program will continue operating mostly normally.
Warn = Level{Name: "WARN", Color: 33, Severity: 50}
// Error is the level saying that something went wrong and the program may not operate as expected, but will still continue.
Error = Level{Name: "ERROR", Color: 31, Severity: 100}
// Fatal is the level saying that something went wrong and the program will not operate normally.
Fatal = Level{Name: "FATAL", Color: 35, Severity: 9001}
)
// PrintLevel tells the first severity level at which messages should be printed to stdout
var PrintLevel = 10
// PrintDebug means PrintLevel = 0, kept for backwards compatibility
var PrintDebug = false
// FileTimeformat is the time format used in log file names.
var FileTimeformat = "2006-01-02"
// FileformatArgs is an undocumented integer.
var FileformatArgs = 3
// Fileformat is the format used for log file names.
var Fileformat = func(now string, i int) string { return fmt.Sprintf("%[1]s-%02[2]d.log", now, i) }
// Timeformat is the time format used in logging.
var Timeformat = "15:04:05 02.01.2006"
var writer *bufio.Writer
var lines int
// InitWithWriter initializes MauLogger with the given writer.
func InitWithWriter(w *bufio.Writer) {
writer = w
}
// Init initializes MauLogger.
func Init() {
// Find the next file name.
now := time.Now().Format(FileTimeformat)
i := 1
for ; ; i++ {
if _, err := os.Stat(Fileformat(now, i)); os.IsNotExist(err) {
break
}
if i == 99 {
i = 1
break
}
}
// Open the file
file, err := os.OpenFile(Fileformat(now, i), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0700)
if err != nil {
panic(err)
}
if file == nil {
panic(os.ErrInvalid)
}
// Create a writer
writer = bufio.NewWriter(file)
}
// Debugf formats and logs a debug message.
func Debugf(message string, args ...interface{}) {
logln(Debug, fmt.Sprintf(message, args...))
}
// Printf formats and logs a string in the Info log level.
func Printf(message string, args ...interface{}) {
Infof(message, args...)
}
// Infof formats and logs a string in the Info log level.
func Infof(message string, args ...interface{}) {
logln(Info, fmt.Sprintf(message, args...))
}
// Warnf formats and logs a string in the Warn log level.
func Warnf(message string, args ...interface{}) {
logln(Warn, fmt.Sprintf(message, args...))
}
// Errorf formats and logs a string in the Error log level.
func Errorf(message string, args ...interface{}) {
logln(Error, fmt.Sprintf(message, args...))
}
// Fatalf formats and logs a string in the Fatal log level.
func Fatalf(message string, args ...interface{}) {
logln(Fatal, fmt.Sprintf(message, args...))
}
// Logf formats and logs a message in the given log level.
func Logf(level Level, message string, args ...interface{}) {
logln(level, fmt.Sprintf(message, args...))
}
// Debugln logs a debug message.
func Debugln(args ...interface{}) {
log(Debug, fmt.Sprintln(args...))
}
// Println logs a string in the Info log level.
func Println(args ...interface{}) {
Infoln(args...)
}
// Infoln logs a string in the Info log level.
func Infoln(args ...interface{}) {
log(Info, fmt.Sprintln(args...))
}
// Warnln logs a string in the Warn log level.
func Warnln(args ...interface{}) {
log(Warn, fmt.Sprintln(args...))
}
// Errorln logs a string in the Error log level.
func Errorln(args ...interface{}) {
log(Error, fmt.Sprintln(args...))
}
// Fatalln logs a string in the Fatal log level.
func Fatalln(args ...interface{}) {
log(Fatal, fmt.Sprintln(args...))
}
// Logln logs a message in the given log level.
func Logln(level Level, args ...interface{}) {
log(level, fmt.Sprintln(args...))
}
func logln(level Level, message string) {
log(level, fmt.Sprintln(message))
}
func log(level Level, message string) {
// Prefix the message with the timestamp and log level.
msg := []byte(fmt.Sprintf("[%[1]s] [%[2]s] %[3]s", time.Now().Format(Timeformat), level.Name, message))
if writer != nil {
// Write it to the log file.
_, err := writer.Write(msg)
if err != nil {
panic(err)
}
lines++
// Flush the file if needed
if lines == 5 {
lines = 0
writer.Flush()
}
}
// Print to stdout using correct color
if level.Severity >= PrintLevel || PrintDebug {
if level.Severity >= Error.Severity {
os.Stderr.Write(level.GetColor())
os.Stderr.Write(msg)
os.Stderr.Write(level.GetReset())
} else {
os.Stdout.Write(level.GetColor())
os.Stdout.Write(msg)
os.Stdout.Write(level.GetReset())
}
}
}
// Shutdown cleans up the logger.
func Shutdown() {
if writer != nil {
writer.Flush()
}
}