From d170626154a4853be386c7180050afe09be3ca0b Mon Sep 17 00:00:00 2001 From: xengineering Date: Wed, 28 Jun 2023 10:56:53 +0200 Subject: xmpp: Implement basic elementBuffer This is needed to buffer XML elements of a stream until they are complete and can be given to an element handler. --- xmpp/element_buffer.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ xmpp/stream_pair.go | 20 +++++++++++++++-- 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 xmpp/element_buffer.go diff --git a/xmpp/element_buffer.go b/xmpp/element_buffer.go new file mode 100644 index 0000000..c55279b --- /dev/null +++ b/xmpp/element_buffer.go @@ -0,0 +1,58 @@ +package xmpp + +import ( + "encoding/xml" +) + +// elementBuffer is a struct to store multiple values of type xml.Token until +// they form a complete XML element with opening and closing tag which is +// suitable to be passed to an appropriate handler function. +type elementBuffer struct { + tokens []xml.Token + end xml.EndElement + level int +} + +// newElementBuffer returns a new initialized elementBuffer struct. +func newElementBuffer() elementBuffer { + buf := elementBuffer{} + buf.reset() + return buf +} + +// FIXME this function needs essential error handling for corner cases! +// +// add is able to add a new xml.Token to the buffer. There are some rules +// checked to ensure a correct and consistent elementBuffer which are checked. +// If one of these checks fail the token is not added and a corresponding error +// is returned. +func (e *elementBuffer) add(t xml.Token) error { + switch t.(type) { + case xml.StartElement: + e.level += 1 + case xml.EndElement: + e.level -= 1 + } + e.tokens = append(e.tokens, t) + return nil +} + +// isComplete returns true if the buffer contains a slice of XML tokens which +// form a complete XML element starting with an xml.StartElement and closing +// with the corresponding xml.EndElement. +func (e *elementBuffer) isComplete() bool { + return (len(e.tokens) > 0 && e.level == 0) +} + +// reset returns the content of the buffer as a slice of XML tokens and resets +// the buffer to the initial state. This function can be used to initialize the +// elementBuffer struct. In that case the return value can be ignored. +func (e *elementBuffer) reset() (buf []xml.Token) { + retval := e.tokens + + e.tokens = make([]xml.Token, 0) + e.end = xml.EndElement{} + e.level = 0 + + return retval +} diff --git a/xmpp/stream_pair.go b/xmpp/stream_pair.go index e02cca6..b13d8a3 100644 --- a/xmpp/stream_pair.go +++ b/xmpp/stream_pair.go @@ -9,6 +9,8 @@ func runStreamPair(s *session) { openStream(s) defer closeStream(s) + buf := newElementBuffer() + for { select { case data := <-s.in: @@ -18,8 +20,22 @@ func runStreamPair(s *session) { default: log.Printf("Unknown data '%d'!\n", data) } - case _ = <-s.rx: - // TODO route received XML token here + case t := <-s.rx: + err := buf.add(t) + if err != nil { + log.Printf("Could not add XML token to buffer: %v\n", err) + return + } + if buf.isComplete() { + element := buf.reset() + // TODO handle XML element here - this is just a dummy: + switch start := element[0].(type) { + case xml.StartElement: + log.Printf("Got XML element `%s`\n", start.Name.Local) + default: + log.Println("No xml.StartElement at start of element buffer!") + } + } } } } -- cgit v1.2.3-70-g09d2 From 64d71d587eb618cb4490e51d4af48e9f374f7f8d Mon Sep 17 00:00:00 2001 From: xengineering Date: Wed, 28 Jun 2023 17:43:38 +0200 Subject: xmpp: Add indent level test for elementBuffer This tests if the indent level is correctly detected. This basic test can be extended to support invalid XML elements which should be refused to add to the element buffer. --- xmpp/element_buffer_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 xmpp/element_buffer_test.go diff --git a/xmpp/element_buffer_test.go b/xmpp/element_buffer_test.go new file mode 100644 index 0000000..3d03f0b --- /dev/null +++ b/xmpp/element_buffer_test.go @@ -0,0 +1,40 @@ +package xmpp + +import ( + "encoding/xml" + "strings" + "testing" +) + +type bufTest struct { + xml string + levels []int +} + +func TestElementBuffer(t *testing.T) { + var tests = []bufTest{ + bufTest{``, []int{1, 0}}, + bufTest{`testing`, []int{1, 2, 2, 1, 0}}, + bufTest{`testing`, []int{1, 1, 2, 2, 1, 0}}, + } + + for _, v := range tests { + r := strings.NewReader(v.xml) + d := xml.NewDecoder(r) + b := newElementBuffer() + + for i, _ := range v.levels { + token, err := d.Token() + if err != nil { + break + } + err = b.add(token) + if err != nil { + t.Fatalf("xmpp.elementBuffer.add() returned error: `%v`!\n", err) + } + if b.level != v.levels[i] { + t.Fatalf("Indent level of xmpp.elementBuffer %d does not match value given by test data %d!\n", b.level, v.levels[i]) + } + } + } +} -- cgit v1.2.3-70-g09d2 From 2e574362a25c5de44b2321a48a38d189e1f54930 Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 29 Jun 2023 20:01:22 +0200 Subject: Make tests verbose This helps to debug tests with t.Log() and t.Logf(). --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7db9cae..5e8f23e 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,6 @@ install: $(LIMOX) install -Dm 644 $(DESKTOP) $(DESKTOP_INSTALL) tests: - go test $(LIMOX_GO_PACKAGE)/... + go test -test.v $(LIMOX_GO_PACKAGE)/... imgs: $(IMGS) -- cgit v1.2.3-70-g09d2 From 81680ddb86d406ccaf4ba8d656bd27b1bbcf8367 Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 29 Jun 2023 21:30:56 +0200 Subject: Minor improvements to element buffer test --- xmpp/element_buffer_test.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/xmpp/element_buffer_test.go b/xmpp/element_buffer_test.go index 3d03f0b..4b5700c 100644 --- a/xmpp/element_buffer_test.go +++ b/xmpp/element_buffer_test.go @@ -12,10 +12,12 @@ type bufTest struct { } func TestElementBuffer(t *testing.T) { - var tests = []bufTest{ + tests := []bufTest{ bufTest{``, []int{1, 0}}, + bufTest{``, []int{1, 0}}, bufTest{`testing`, []int{1, 2, 2, 1, 0}}, bufTest{`testing`, []int{1, 1, 2, 2, 1, 0}}, + bufTest{`testing`, []int{0, 1, 2, 2, 1, 0}}, } for _, v := range tests { @@ -23,18 +25,27 @@ func TestElementBuffer(t *testing.T) { d := xml.NewDecoder(r) b := newElementBuffer() - for i, _ := range v.levels { + i := 0 + + for { token, err := d.Token() if err != nil { + if i != len(v.levels) { + t.Fatalf("Stopped parsing at unexpected index due to error `%v`!\n", err) + } break } + err = b.add(token) if err != nil { - t.Fatalf("xmpp.elementBuffer.add() returned error: `%v`!\n", err) + t.Fatalf("add(token) failed with error `%v`!\n", err) } + if b.level != v.levels[i] { t.Fatalf("Indent level of xmpp.elementBuffer %d does not match value given by test data %d!\n", b.level, v.levels[i]) } + + i += 1 } } } -- cgit v1.2.3-70-g09d2 From 4e09d963a5222fbda64fa8c1e333beabe77f9355 Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 29 Jun 2023 21:35:33 +0200 Subject: Document element buffer test point struct This makes it easier to add further test data in case there are further corner cases which should be tested in the future. --- xmpp/element_buffer_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xmpp/element_buffer_test.go b/xmpp/element_buffer_test.go index 4b5700c..113a2c4 100644 --- a/xmpp/element_buffer_test.go +++ b/xmpp/element_buffer_test.go @@ -6,6 +6,10 @@ import ( "testing" ) +// bufTest is a struct containing a test point for the +// xengineering.eu/limox/xmpp.elementBuffer. It contains a test XML string +// which has to be exactly one XML element and an array of indentation levels +// which have to be checked after each token which is parsed. type bufTest struct { xml string levels []int -- cgit v1.2.3-70-g09d2 From 3df1a88c726c08704e8b71c467bd8e11c9a52db6 Mon Sep 17 00:00:00 2001 From: xengineering Date: Thu, 29 Jun 2023 21:36:26 +0200 Subject: Add FIXME to element buffer specification The behaviour is ok for now but should be improved in the future to make it more robust. --- xmpp/element_buffer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xmpp/element_buffer.go b/xmpp/element_buffer.go index c55279b..7792db2 100644 --- a/xmpp/element_buffer.go +++ b/xmpp/element_buffer.go @@ -37,6 +37,9 @@ func (e *elementBuffer) add(t xml.Token) error { return nil } +// FIXME isComplete would be true if a stream with only one XML comment is +// passed to the buffer. This might be unexpected behaviour. +// // isComplete returns true if the buffer contains a slice of XML tokens which // form a complete XML element starting with an xml.StartElement and closing // with the corresponding xml.EndElement. -- cgit v1.2.3-70-g09d2