diff options
| -rw-r--r-- | xmpp/router.go | 59 | ||||
| -rw-r--r-- | xmpp/router_test.go | 69 | ||||
| -rw-r--r-- | xmpp/stream_pair.go | 8 | 
3 files changed, 129 insertions, 7 deletions
diff --git a/xmpp/router.go b/xmpp/router.go new file mode 100644 index 0000000..839f870 --- /dev/null +++ b/xmpp/router.go @@ -0,0 +1,59 @@ +package xmpp + +import ( +	"encoding/xml" +	"log" +) + +// routingTable is a data structure which contains routing information for XML +// elements. The xml.StartElement at the beginning of an XML element has a name +// containing the XML namespace and a local name. Based on this compisition +// which forms the xml.Name the appropriate handler function is defined by each +// entry of the routingTable. +type routingTable []struct { +	name    xml.Name +	handler func([]xml.Token) +} + +// getRoutingTable returns the routing table used in +// xengineering.eu/limox/xmpp. Since Go does not allow such a datatype as a +// constant such a function is a simple yet inefficient approach to guarantee +// that an unmodified routing table is delivered to each user. A global +// variable would have the problem that it could be altered during execution. +func getRoutingTable() routingTable { +	return routingTable{ +		// TODO fill with entries +	} +} + +// route determines the correct handler function for the given XML element by a +// given routingTable. In addition it executes the determined handler function. +// If no handler function is found an error message is send via the log module. +func route(e []xml.Token, t routingTable) { +	var name xml.Name + +	// TODO a stronger definition of an XML element (as here +	// https://www.w3schools.com/xml/xml_elements.asp) would define that the +	// first Token of an element is a StartElement token. This would make this +	// code easier. +	escape := false +	for _, token := range e { +		switch s := token.(type) { +		case xml.StartElement: +			name = s.Name +			escape = true +		} +		if escape { +			break +		} +	} + +	for _, r := range t { +		if name == r.name { +			r.handler(e) +			return +		} +	} + +	log.Println("Could not route XML element") +} diff --git a/xmpp/router_test.go b/xmpp/router_test.go new file mode 100644 index 0000000..ea13712 --- /dev/null +++ b/xmpp/router_test.go @@ -0,0 +1,69 @@ +package xmpp + +import ( +	"encoding/xml" +	"testing" +	"strings" +) + +// routerTest contains a single test case for the xmpp.router. The XML element +// is given as a string and the value int describes the expected value of a +// test variable inside the corresponding unit test. See TestRouter for +// details. +type routerTest struct { +	xml     string +	value   int +} + +// TestRouter tests the xmpp/router.go file. The central functionality is the +// route() function. To test this a fake routing table is created inside this +// test with XML element handler function produced by the factory function +// defined below. each handler simply sets the value of the testpoint variable +// to the value mentioned by the test point. That way the routing can be +// validated. +func TestRouter(t *testing.T) { +	var testpoint int + +	factory := func(tp *int, i int) func([]xml.Token) { +		return func([]xml.Token) { +			*tp = i +		} +	} + +	tests := []routerTest{ +		routerTest{`<a></a>`, 1}, +		routerTest{`<b></b>`, 2}, +		routerTest{`<c></c>`, 3}, +		routerTest{`<b xmlns='https://xengineering.eu'></b>`, 4}, +		routerTest{`<a xmlns='https://xengineering.eu'></a>`, 5}, +	} + +	testRouting := routingTable{ +		{xml.Name{``, `a`}, factory(&testpoint, 1)}, +		{xml.Name{``, `b`}, factory(&testpoint, 2)}, +		{xml.Name{``, `c`}, factory(&testpoint, 3)}, +		{xml.Name{`https://xengineering.eu`, `b`}, factory(&testpoint, 4)}, +		{xml.Name{`https://xengineering.eu`, `a`}, factory(&testpoint, 5)}, +	} + +	for _, v := range tests { +		testpoint = 0 + +		r := strings.NewReader(v.xml) +		d := xml.NewDecoder(r) +		tokens := make([]xml.Token, 0) +		for { +			token, err := d.Token() +			if err != nil { +				break +			} +			tokens = append(tokens, xml.CopyToken(token)) +		} + +		route(tokens, testRouting) + +		if testpoint != v.value { +			t.Fatalf("XML element was not routed correctly!\n") +		} +	} +} diff --git a/xmpp/stream_pair.go b/xmpp/stream_pair.go index b13d8a3..c9108d1 100644 --- a/xmpp/stream_pair.go +++ b/xmpp/stream_pair.go @@ -28,13 +28,7 @@ func runStreamPair(s *session) {  			}  			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!") -				} +				route(element, getRoutingTable())  			}  		}  	}  | 
