mirror of
https://github.com/FluuxIO/go-xmpp.git
synced 2025-11-09 03:23:43 -08:00
Refactor tests
This commit is contained in:
32
stanza/fifo_queue.go
Normal file
32
stanza/fifo_queue.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package stanza
|
||||
|
||||
// FIFO queue for string contents
|
||||
// Implementations have no guarantee regarding thread safety !
|
||||
type FifoQueue interface {
|
||||
// Pop returns the first inserted element still in queue and delete it from queue
|
||||
// No guarantee regarding thread safety !
|
||||
Pop() Queueable
|
||||
|
||||
// PopN returns the N first inserted elements still in queue and delete them from queue
|
||||
// No guarantee regarding thread safety !
|
||||
PopN(i int) []Queueable
|
||||
|
||||
// Peek returns a copy of the first inserted element in queue without deleting it
|
||||
// No guarantee regarding thread safety !
|
||||
Peek() Queueable
|
||||
|
||||
// Peek returns a copy of the first inserted element in queue without deleting it
|
||||
// No guarantee regarding thread safety !
|
||||
PeekN() []Queueable
|
||||
// Push adds an element to the queue
|
||||
// No guarantee regarding thread safety !
|
||||
Push(s Queueable) error
|
||||
|
||||
// Empty returns true if queue is empty
|
||||
// No guarantee regarding thread safety !
|
||||
Empty() bool
|
||||
}
|
||||
|
||||
type Queueable interface {
|
||||
QueueableName() string
|
||||
}
|
||||
@@ -93,8 +93,8 @@ func (b *Bind) GetSet() *ResultSet {
|
||||
// This is the draft defining how to handle the transition:
|
||||
// https://tools.ietf.org/html/draft-cridland-xmpp-session-01
|
||||
type StreamSession struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-session session"`
|
||||
Optional bool // If element does exist, it mean we are not required to open session
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-session session"`
|
||||
Optional *struct{} // If element does exist, it mean we are not required to open session
|
||||
// Result sets
|
||||
ResultSet *ResultSet `xml:"set,omitempty"`
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (s *StreamSession) GetSet() *ResultSet {
|
||||
|
||||
func (s *StreamSession) IsOptional() bool {
|
||||
if s.XMLName.Local == "session" {
|
||||
return s.Optional
|
||||
return s.Optional != nil
|
||||
}
|
||||
// If session element is missing, then we should not use session
|
||||
return true
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Check that we can detect optional session from advertised stream features
|
||||
func TestSessionFeatures(t *testing.T) {
|
||||
streamFeatures := stanza.StreamFeatures{Session: stanza.StreamSession{Optional: true}}
|
||||
streamFeatures := stanza.StreamFeatures{Session: stanza.StreamSession{Optional: &struct{}{}}}
|
||||
|
||||
data, err := xml.Marshal(streamFeatures)
|
||||
if err != nil {
|
||||
@@ -32,7 +32,7 @@ func TestSessionIQ(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create IQ: %v", err)
|
||||
}
|
||||
iq.Payload = &stanza.StreamSession{XMLName: xml.Name{Local: "session"}, Optional: true}
|
||||
iq.Payload = &stanza.StreamSession{XMLName: xml.Name{Local: "session"}, Optional: &struct{}{}}
|
||||
|
||||
data, err := xml.Marshal(iq)
|
||||
if err != nil {
|
||||
|
||||
171
stanza/stanza_errors.go
Normal file
171
stanza/stanza_errors.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package stanza
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
type StanzaErrorGroup interface {
|
||||
GroupErrorName() string
|
||||
}
|
||||
|
||||
type BadFormat struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas bad-format"`
|
||||
}
|
||||
|
||||
func (e *BadFormat) GroupErrorName() string { return "bad-format" }
|
||||
|
||||
type BadNamespacePrefix struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas bad-namespace-prefix"`
|
||||
}
|
||||
|
||||
func (e *BadNamespacePrefix) GroupErrorName() string { return "bad-namespace-prefix" }
|
||||
|
||||
type Conflict struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas conflict"`
|
||||
}
|
||||
|
||||
func (e *Conflict) GroupErrorName() string { return "conflict" }
|
||||
|
||||
type ConnectionTimeout struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas connection-timeout"`
|
||||
}
|
||||
|
||||
func (e *ConnectionTimeout) GroupErrorName() string { return "connection-timeout" }
|
||||
|
||||
type HostGone struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas host-gone"`
|
||||
}
|
||||
|
||||
func (e *HostGone) GroupErrorName() string { return "host-gone" }
|
||||
|
||||
type HostUnknown struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas host-unknown"`
|
||||
}
|
||||
|
||||
func (e *HostUnknown) GroupErrorName() string { return "host-unknown" }
|
||||
|
||||
type ImproperAddressing struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas improper-addressing"`
|
||||
}
|
||||
|
||||
func (e *ImproperAddressing) GroupErrorName() string { return "improper-addressing" }
|
||||
|
||||
type InternalServerError struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas internal-server-error"`
|
||||
}
|
||||
|
||||
func (e *InternalServerError) GroupErrorName() string { return "internal-server-error" }
|
||||
|
||||
type InvalidForm struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas invalid-from"`
|
||||
}
|
||||
|
||||
func (e *InvalidForm) GroupErrorName() string { return "invalid-from" }
|
||||
|
||||
type InvalidId struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas invalid-id"`
|
||||
}
|
||||
|
||||
func (e *InvalidId) GroupErrorName() string { return "invalid-id" }
|
||||
|
||||
type InvalidNamespace struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas invalid-namespace"`
|
||||
}
|
||||
|
||||
func (e *InvalidNamespace) GroupErrorName() string { return "invalid-namespace" }
|
||||
|
||||
type InvalidXML struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas invalid-xml"`
|
||||
}
|
||||
|
||||
func (e *InvalidXML) GroupErrorName() string { return "invalid-xml" }
|
||||
|
||||
type NotAuthorized struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas not-authorized"`
|
||||
}
|
||||
|
||||
func (e *NotAuthorized) GroupErrorName() string { return "not-authorized" }
|
||||
|
||||
type NotWellFormed struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas not-well-formed"`
|
||||
}
|
||||
|
||||
func (e *NotWellFormed) GroupErrorName() string { return "not-well-formed" }
|
||||
|
||||
type PolicyViolation struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas policy-violation"`
|
||||
}
|
||||
|
||||
func (e *PolicyViolation) GroupErrorName() string { return "policy-violation" }
|
||||
|
||||
type RemoteConnectionFailed struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas remote-connection-failed"`
|
||||
}
|
||||
|
||||
func (e *RemoteConnectionFailed) GroupErrorName() string { return "remote-connection-failed" }
|
||||
|
||||
type Reset struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas reset"`
|
||||
}
|
||||
|
||||
func (e *Reset) GroupErrorName() string { return "reset" }
|
||||
|
||||
type ResourceConstraint struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas resource-constraint"`
|
||||
}
|
||||
|
||||
func (e *ResourceConstraint) GroupErrorName() string { return "resource-constraint" }
|
||||
|
||||
type RestrictedXML struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas restricted-xml"`
|
||||
}
|
||||
|
||||
func (e *RestrictedXML) GroupErrorName() string { return "restricted-xml" }
|
||||
|
||||
type SeeOtherHost struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas see-other-host"`
|
||||
}
|
||||
|
||||
func (e *SeeOtherHost) GroupErrorName() string { return "see-other-host" }
|
||||
|
||||
type SystemShutdown struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas system-shutdown"`
|
||||
}
|
||||
|
||||
func (e *SystemShutdown) GroupErrorName() string { return "system-shutdown" }
|
||||
|
||||
type UndefinedCondition struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas undefined-condition"`
|
||||
}
|
||||
|
||||
func (e *UndefinedCondition) GroupErrorName() string { return "undefined-condition" }
|
||||
|
||||
type UnsupportedEncoding struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas unsupported-encoding"`
|
||||
}
|
||||
|
||||
type UnexpectedRequest struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas unexpected-request"`
|
||||
}
|
||||
|
||||
func (e *UnexpectedRequest) GroupErrorName() string { return "unexpected-request" }
|
||||
|
||||
func (e *UnsupportedEncoding) GroupErrorName() string { return "unsupported-encoding" }
|
||||
|
||||
type UnsupportedStanzaType struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas unsupported-stanza-type"`
|
||||
}
|
||||
|
||||
func (e *UnsupportedStanzaType) GroupErrorName() string { return "unsupported-stanza-type" }
|
||||
|
||||
type UnsupportedVersion struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas unsupported-version"`
|
||||
}
|
||||
|
||||
func (e *UnsupportedVersion) GroupErrorName() string { return "unsupported-version" }
|
||||
|
||||
type XMLNotWellFormed struct {
|
||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-stanzas xml-not-well-formed"`
|
||||
}
|
||||
|
||||
func (e *XMLNotWellFormed) GroupErrorName() string { return "xml-not-well-formed" }
|
||||
@@ -118,6 +118,10 @@ type streamManagement struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 sm"`
|
||||
}
|
||||
|
||||
func (streamManagement) Name() string {
|
||||
return "streamManagement"
|
||||
}
|
||||
|
||||
func (sf *StreamFeatures) DoesStreamManagement() (isSupported bool) {
|
||||
if sf.StreamManagement.XMLName.Space+" "+sf.StreamManagement.XMLName.Local == "urn:xmpp:sm:3 sm" {
|
||||
return true
|
||||
|
||||
@@ -3,12 +3,19 @@ package stanza
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
NSStreamManagement = "urn:xmpp:sm:3"
|
||||
)
|
||||
|
||||
type SMEnable struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 enable"`
|
||||
Max *uint `xml:"max,attr,omitempty"`
|
||||
Resume *bool `xml:"resume,attr,omitempty"`
|
||||
}
|
||||
|
||||
// Enabled as defined in Stream Management spec
|
||||
// Reference: https://xmpp.org/extensions/xep-0198.html#enable
|
||||
type SMEnabled struct {
|
||||
@@ -23,6 +30,94 @@ func (SMEnabled) Name() string {
|
||||
return "Stream Management: enabled"
|
||||
}
|
||||
|
||||
type UnAckQueue struct {
|
||||
Uslice []*UnAckedStz
|
||||
sync.RWMutex
|
||||
}
|
||||
type UnAckedStz struct {
|
||||
Id int
|
||||
Stz string
|
||||
}
|
||||
|
||||
func NewUnAckQueue() *UnAckQueue {
|
||||
return &UnAckQueue{
|
||||
Uslice: make([]*UnAckedStz, 0, 10), // Capacity is 0 to comply with "Push" implementation (so that no reachable element is nil)
|
||||
RWMutex: sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UnAckedStz) QueueableName() string {
|
||||
return "Un-acknowledged stanza"
|
||||
}
|
||||
|
||||
func (uaq *UnAckQueue) PeekN(n int) []Queueable {
|
||||
if n <= 0 {
|
||||
return []Queueable{}
|
||||
}
|
||||
if len(uaq.Uslice) < n {
|
||||
n = len(uaq.Uslice)
|
||||
}
|
||||
|
||||
if len(uaq.Uslice) == 0 {
|
||||
return []Queueable{}
|
||||
}
|
||||
var r []Queueable
|
||||
for i := 0; i < n; i++ {
|
||||
r = append(r, uaq.Uslice[i])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// No guarantee regarding thread safety !
|
||||
func (uaq *UnAckQueue) Pop() Queueable {
|
||||
r := uaq.Peek()
|
||||
if r != nil {
|
||||
uaq.Uslice = uaq.Uslice[1:]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// No guarantee regarding thread safety !
|
||||
func (uaq *UnAckQueue) PopN(n int) []Queueable {
|
||||
r := uaq.PeekN(n)
|
||||
uaq.Uslice = uaq.Uslice[len(r):]
|
||||
return r
|
||||
}
|
||||
|
||||
func (uaq *UnAckQueue) Peek() Queueable {
|
||||
if len(uaq.Uslice) == 0 {
|
||||
return nil
|
||||
}
|
||||
r := uaq.Uslice[0]
|
||||
return r
|
||||
}
|
||||
|
||||
func (uaq *UnAckQueue) Push(s Queueable) error {
|
||||
pushIdx := 1
|
||||
if len(uaq.Uslice) != 0 {
|
||||
pushIdx = uaq.Uslice[len(uaq.Uslice)-1].Id + 1
|
||||
}
|
||||
|
||||
sStz, ok := s.(*UnAckedStz)
|
||||
if !ok {
|
||||
return errors.New("element in not compatible with this queue. expected an UnAckedStz")
|
||||
}
|
||||
|
||||
e := UnAckedStz{
|
||||
Id: pushIdx,
|
||||
Stz: sStz.Stz,
|
||||
}
|
||||
|
||||
uaq.Uslice = append(uaq.Uslice, &e)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uaq *UnAckQueue) Empty() bool {
|
||||
r := len(uaq.Uslice)
|
||||
return r == 0
|
||||
}
|
||||
|
||||
// Request as defined in Stream Management spec
|
||||
// Reference: https://xmpp.org/extensions/xep-0198.html#acking
|
||||
type SMRequest struct {
|
||||
@@ -37,7 +132,7 @@ func (SMRequest) Name() string {
|
||||
// Reference: https://xmpp.org/extensions/xep-0198.html#acking
|
||||
type SMAnswer struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 a"`
|
||||
H uint `xml:"h,attr,omitempty"`
|
||||
H uint `xml:"h,attr"`
|
||||
}
|
||||
|
||||
func (SMAnswer) Name() string {
|
||||
@@ -49,24 +144,175 @@ func (SMAnswer) Name() string {
|
||||
type SMResumed struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 resumed"`
|
||||
PrevId string `xml:"previd,attr,omitempty"`
|
||||
H uint `xml:"h,attr,omitempty"`
|
||||
H *uint `xml:"h,attr,omitempty"`
|
||||
}
|
||||
|
||||
func (SMResumed) Name() string {
|
||||
return "Stream Management: resumed"
|
||||
}
|
||||
|
||||
// Resumed as defined in Stream Management spec
|
||||
// Reference: https://xmpp.org/extensions/xep-0198.html#acking
|
||||
type SMResume struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 resume"`
|
||||
PrevId string `xml:"previd,attr,omitempty"`
|
||||
H *uint `xml:"h,attr,omitempty"`
|
||||
}
|
||||
|
||||
func (SMResume) Name() string {
|
||||
return "Stream Management: resume"
|
||||
}
|
||||
|
||||
// Failed as defined in Stream Management spec
|
||||
// Reference: https://xmpp.org/extensions/xep-0198.html#acking
|
||||
type SMFailed struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:sm:3 failed"`
|
||||
// TODO: Handle decoding error cause (need custom parsing).
|
||||
H *uint `xml:"h,attr,omitempty"`
|
||||
|
||||
StreamErrorGroup StanzaErrorGroup
|
||||
}
|
||||
|
||||
func (SMFailed) Name() string {
|
||||
return "Stream Management: failed"
|
||||
}
|
||||
|
||||
func (smf *SMFailed) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
smf.XMLName = start.Name
|
||||
|
||||
// According to https://xmpp.org/rfcs/rfc3920.html#def we should have no attributes aside from the namespace
|
||||
// which we don't use internally
|
||||
|
||||
// decode inner elements
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tt := t.(type) {
|
||||
|
||||
case xml.StartElement:
|
||||
// Decode sub-elements
|
||||
var err error
|
||||
switch tt.Name.Local {
|
||||
case "bad-format":
|
||||
bf := BadFormat{}
|
||||
err = d.DecodeElement(&bf, &tt)
|
||||
smf.StreamErrorGroup = &bf
|
||||
case "bad-namespace-prefix":
|
||||
bnp := BadNamespacePrefix{}
|
||||
err = d.DecodeElement(&bnp, &tt)
|
||||
smf.StreamErrorGroup = &bnp
|
||||
case "conflict":
|
||||
c := Conflict{}
|
||||
err = d.DecodeElement(&c, &tt)
|
||||
smf.StreamErrorGroup = &c
|
||||
case "connection-timeout":
|
||||
ct := ConnectionTimeout{}
|
||||
err = d.DecodeElement(&ct, &tt)
|
||||
smf.StreamErrorGroup = &ct
|
||||
case "host-gone":
|
||||
hg := HostGone{}
|
||||
err = d.DecodeElement(&hg, &tt)
|
||||
smf.StreamErrorGroup = &hg
|
||||
case "host-unknown":
|
||||
hu := HostUnknown{}
|
||||
err = d.DecodeElement(&hu, &tt)
|
||||
smf.StreamErrorGroup = &hu
|
||||
case "improper-addressing":
|
||||
ia := ImproperAddressing{}
|
||||
err = d.DecodeElement(&ia, &tt)
|
||||
smf.StreamErrorGroup = &ia
|
||||
case "internal-server-error":
|
||||
ise := InternalServerError{}
|
||||
err = d.DecodeElement(&ise, &tt)
|
||||
smf.StreamErrorGroup = &ise
|
||||
case "invalid-from":
|
||||
ifrm := InvalidForm{}
|
||||
err = d.DecodeElement(&ifrm, &tt)
|
||||
smf.StreamErrorGroup = &ifrm
|
||||
case "invalid-id":
|
||||
id := InvalidId{}
|
||||
err = d.DecodeElement(&id, &tt)
|
||||
smf.StreamErrorGroup = &id
|
||||
case "invalid-namespace":
|
||||
ins := InvalidNamespace{}
|
||||
err = d.DecodeElement(&ins, &tt)
|
||||
smf.StreamErrorGroup = &ins
|
||||
case "invalid-xml":
|
||||
ix := InvalidXML{}
|
||||
err = d.DecodeElement(&ix, &tt)
|
||||
smf.StreamErrorGroup = &ix
|
||||
case "not-authorized":
|
||||
na := NotAuthorized{}
|
||||
err = d.DecodeElement(&na, &tt)
|
||||
smf.StreamErrorGroup = &na
|
||||
case "not-well-formed":
|
||||
nwf := NotWellFormed{}
|
||||
err = d.DecodeElement(&nwf, &tt)
|
||||
smf.StreamErrorGroup = &nwf
|
||||
case "policy-violation":
|
||||
pv := PolicyViolation{}
|
||||
err = d.DecodeElement(&pv, &tt)
|
||||
smf.StreamErrorGroup = &pv
|
||||
case "remote-connection-failed":
|
||||
rcf := RemoteConnectionFailed{}
|
||||
err = d.DecodeElement(&rcf, &tt)
|
||||
smf.StreamErrorGroup = &rcf
|
||||
case "resource-constraint":
|
||||
rc := ResourceConstraint{}
|
||||
err = d.DecodeElement(&rc, &tt)
|
||||
smf.StreamErrorGroup = &rc
|
||||
case "restricted-xml":
|
||||
rx := RestrictedXML{}
|
||||
err = d.DecodeElement(&rx, &tt)
|
||||
smf.StreamErrorGroup = &rx
|
||||
case "see-other-host":
|
||||
soh := SeeOtherHost{}
|
||||
err = d.DecodeElement(&soh, &tt)
|
||||
smf.StreamErrorGroup = &soh
|
||||
case "system-shutdown":
|
||||
ss := SystemShutdown{}
|
||||
err = d.DecodeElement(&ss, &tt)
|
||||
smf.StreamErrorGroup = &ss
|
||||
case "undefined-condition":
|
||||
uc := UndefinedCondition{}
|
||||
err = d.DecodeElement(&uc, &tt)
|
||||
smf.StreamErrorGroup = &uc
|
||||
case "unexpected-request":
|
||||
ur := UnexpectedRequest{}
|
||||
err = d.DecodeElement(&ur, &tt)
|
||||
smf.StreamErrorGroup = &ur
|
||||
case "unsupported-encoding":
|
||||
ue := UnsupportedEncoding{}
|
||||
err = d.DecodeElement(&ue, &tt)
|
||||
smf.StreamErrorGroup = &ue
|
||||
case "unsupported-stanza-type":
|
||||
ust := UnsupportedStanzaType{}
|
||||
err = d.DecodeElement(&ust, &tt)
|
||||
smf.StreamErrorGroup = &ust
|
||||
case "unsupported-version":
|
||||
uv := UnsupportedVersion{}
|
||||
err = d.DecodeElement(&uv, &tt)
|
||||
smf.StreamErrorGroup = &uv
|
||||
case "xml-not-well-formed":
|
||||
xnwf := XMLNotWellFormed{}
|
||||
err = d.DecodeElement(&xnwf, &tt)
|
||||
smf.StreamErrorGroup = &xnwf
|
||||
default:
|
||||
return errors.New("error is unknown")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case xml.EndElement:
|
||||
if tt == start.End() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type smDecoder struct{}
|
||||
|
||||
var sm smDecoder
|
||||
@@ -78,9 +324,11 @@ func (s smDecoder) decode(p *xml.Decoder, se xml.StartElement) (Packet, error) {
|
||||
return s.decodeEnabled(p, se)
|
||||
case "resumed":
|
||||
return s.decodeResumed(p, se)
|
||||
case "resume":
|
||||
return s.decodeResume(p, se)
|
||||
case "r":
|
||||
return s.decodeRequest(p, se)
|
||||
case "h":
|
||||
case "a":
|
||||
return s.decodeAnswer(p, se)
|
||||
case "failed":
|
||||
return s.decodeFailed(p, se)
|
||||
@@ -102,6 +350,11 @@ func (smDecoder) decodeResumed(p *xml.Decoder, se xml.StartElement) (SMResumed,
|
||||
return packet, err
|
||||
}
|
||||
|
||||
func (smDecoder) decodeResume(p *xml.Decoder, se xml.StartElement) (SMResume, error) {
|
||||
var packet SMResume
|
||||
err := p.DecodeElement(&packet, &se)
|
||||
return packet, err
|
||||
}
|
||||
func (smDecoder) decodeRequest(p *xml.Decoder, se xml.StartElement) (SMRequest, error) {
|
||||
var packet SMRequest
|
||||
err := p.DecodeElement(&packet, &se)
|
||||
|
||||
187
stanza/stream_management_test.go
Normal file
187
stanza/stream_management_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package stanza_test
|
||||
|
||||
import (
|
||||
"gosrc.io/xmpp/stanza"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO : tests to add
|
||||
// - Pop on nil or empty slice
|
||||
// - PeekN (normal and too long)
|
||||
|
||||
func TestPushUnack(t *testing.T) {
|
||||
uaq := initUnAckQueue()
|
||||
toPush := stanza.UnAckedStz{
|
||||
Id: 3,
|
||||
Stz: `<iq type='submit'
|
||||
from='confucius@scholars.lit/home'
|
||||
to='registrar.scholars.lit'
|
||||
id='kj3b157n'
|
||||
xml:lang='en'>
|
||||
<query xmlns='jabber:iq:register'>
|
||||
<username>confucius</username>
|
||||
<first>Qui</first>
|
||||
<last>Kong</last>
|
||||
</query>
|
||||
</iq>`,
|
||||
}
|
||||
|
||||
err := uaq.Push(&toPush)
|
||||
if err != nil {
|
||||
t.Fatalf("could not push element to the queue : %v", err)
|
||||
}
|
||||
|
||||
if len(uaq.Uslice) != 4 {
|
||||
t.Fatalf("push to the non-acked queue failed")
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
if uaq.Uslice[i].Id != i+1 {
|
||||
t.Fatalf("indexes were not updated correctly. Expected %d got %d", i, uaq.Uslice[i].Id)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the queue is a fifo : popped element should not be the one we just pushed.
|
||||
popped := uaq.Pop()
|
||||
poppedElt, ok := popped.(*stanza.UnAckedStz)
|
||||
if !ok {
|
||||
t.Fatalf("popped element is not a *stanza.UnAckedStz")
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(*poppedElt, toPush) {
|
||||
t.Fatalf("pushed element is at the top of the fifo queue when it should be at the bottom")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPeekUnack(t *testing.T) {
|
||||
uaq := initUnAckQueue()
|
||||
|
||||
expectedPeek := stanza.UnAckedStz{
|
||||
Id: 1,
|
||||
Stz: `<iq type='set'
|
||||
from='romeo@montague.net/home'
|
||||
to='characters.shakespeare.lit'
|
||||
id='search2'
|
||||
xml:lang='en'>
|
||||
<query xmlns='jabber:iq:search'>
|
||||
<last>Capulet</last>
|
||||
</query>
|
||||
</iq>`,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedPeek, *uaq.Uslice[0]) {
|
||||
t.Fatalf("peek failed to return the correct stanza")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPopNUnack(t *testing.T) {
|
||||
uaq := initUnAckQueue()
|
||||
initLen := len(uaq.Uslice)
|
||||
randPop := rand.Int31n(int32(initLen))
|
||||
|
||||
popped := uaq.PopN(int(randPop))
|
||||
|
||||
if len(uaq.Uslice)+len(popped) != initLen {
|
||||
t.Fatalf("total length changed whith pop n operation : had %d found %d after pop", initLen, len(uaq.Uslice)+len(popped))
|
||||
}
|
||||
|
||||
for _, elt := range popped {
|
||||
for _, oldElt := range uaq.Uslice {
|
||||
if reflect.DeepEqual(elt, oldElt) {
|
||||
t.Fatalf("pop n operation duplicated some elements")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPopNUnackTooLong(t *testing.T) {
|
||||
uaq := initUnAckQueue()
|
||||
initLen := len(uaq.Uslice)
|
||||
|
||||
// Have a random number of elements to pop that's greater than the queue size
|
||||
randPop := rand.Int31n(int32(initLen)) + 1 + int32(initLen)
|
||||
|
||||
popped := uaq.PopN(int(randPop))
|
||||
|
||||
if len(uaq.Uslice)+len(popped) != initLen {
|
||||
t.Fatalf("total length changed whith pop n operation : had %d found %d after pop", initLen, len(uaq.Uslice)+len(popped))
|
||||
}
|
||||
|
||||
for _, elt := range popped {
|
||||
for _, oldElt := range uaq.Uslice {
|
||||
if reflect.DeepEqual(elt, oldElt) {
|
||||
t.Fatalf("pop n operation duplicated some elements")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPopUnack(t *testing.T) {
|
||||
uaq := initUnAckQueue()
|
||||
initLen := len(uaq.Uslice)
|
||||
|
||||
popped := uaq.Pop()
|
||||
|
||||
if len(uaq.Uslice)+1 != initLen {
|
||||
t.Fatalf("total length changed whith pop operation : had %d found %d after pop", initLen, len(uaq.Uslice)+1)
|
||||
}
|
||||
for _, oldElt := range uaq.Uslice {
|
||||
if reflect.DeepEqual(popped, oldElt) {
|
||||
t.Fatalf("pop n operation duplicated some elements")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func initUnAckQueue() stanza.UnAckQueue {
|
||||
q := []*stanza.UnAckedStz{
|
||||
{
|
||||
Id: 1,
|
||||
Stz: `<iq type='set'
|
||||
from='romeo@montague.net/home'
|
||||
to='characters.shakespeare.lit'
|
||||
id='search2'
|
||||
xml:lang='en'>
|
||||
<query xmlns='jabber:iq:search'>
|
||||
<last>Capulet</last>
|
||||
</query>
|
||||
</iq>`,
|
||||
},
|
||||
{Id: 2,
|
||||
Stz: `<iq type='get'
|
||||
from='juliet@capulet.com/balcony'
|
||||
to='characters.shakespeare.lit'
|
||||
id='search3'
|
||||
xml:lang='en'>
|
||||
<query xmlns='jabber:iq:search'/>
|
||||
</iq>`},
|
||||
{Id: 3,
|
||||
Stz: `<iq type='set'
|
||||
from='juliet@capulet.com/balcony'
|
||||
to='characters.shakespeare.lit'
|
||||
id='search4'
|
||||
xml:lang='en'>
|
||||
<query xmlns='jabber:iq:search'>
|
||||
<x xmlns='jabber:x:data' type='submit'>
|
||||
<field type='hidden' var='FORM_TYPE'>
|
||||
<value>jabber:iq:search</value>
|
||||
</field>
|
||||
<field var='x-gender'>
|
||||
<value>male</value>
|
||||
</field>
|
||||
</x>
|
||||
</query>
|
||||
</iq>`},
|
||||
}
|
||||
|
||||
return stanza.UnAckQueue{Uslice: q}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
}
|
||||
Reference in New Issue
Block a user