func handleWebsocket( w http.ResponseWriter, r *http.Request, rep Reporter, renderer render.Renderer, loop time.Duration, ) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { // log.Println("Upgrade:", err) return } defer conn.Close() quit := make(chan struct{}) go func(c *websocket.Conn) { for { // just discard everything the browser sends if _, _, err := c.NextReader(); err != nil { close(quit) break } } }(conn) var ( previousTopo render.RenderableNodes tick = time.Tick(loop) wait = make(chan struct{}, 1) ) rep.WaitOn(wait) defer rep.UnWait(wait) for { newTopo := renderer.Render(rep.Report()).Prune() diff := render.TopoDiff(previousTopo, newTopo) previousTopo = newTopo if err := conn.SetWriteDeadline(time.Now().Add(websocketTimeout)); err != nil { return } if err := conn.WriteJSON(diff); err != nil { return } select { case <-wait: case <-tick: case <-quit: return } } }
func TestTopoDiff(t *testing.T) { nodea := render.RenderableNode{ ID: "nodea", LabelMajor: "Node A", LabelMinor: "'ts an a", Pseudo: false, Node: report.MakeNode().WithAdjacent("nodeb"), } nodeap := nodea nodeap.Adjacency = []string{ "nodeb", "nodeq", // not the same anymore } nodeb := render.RenderableNode{ ID: "nodeb", LabelMajor: "Node B", } // Helper to make RenderableNode maps. nodes := func(ns ...render.RenderableNode) render.RenderableNodes { r := render.RenderableNodes{} for _, n := range ns { r[n.ID] = n } return r } for _, c := range []struct { label string have, want render.Diff }{ { label: "basecase: empty -> something", have: render.TopoDiff(nodes(), nodes(nodea, nodeb)), want: render.Diff{ Add: []render.RenderableNode{nodea, nodeb}, }, }, { label: "basecase: something -> empty", have: render.TopoDiff(nodes(nodea, nodeb), nodes()), want: render.Diff{ Remove: []string{"nodea", "nodeb"}, }, }, { label: "add and remove", have: render.TopoDiff(nodes(nodea), nodes(nodeb)), want: render.Diff{ Add: []render.RenderableNode{nodeb}, Remove: []string{"nodea"}, }, }, { label: "no change", have: render.TopoDiff(nodes(nodea), nodes(nodea)), want: render.Diff{}, }, { label: "change a single node", have: render.TopoDiff(nodes(nodea), nodes(nodeap)), want: render.Diff{ Update: []render.RenderableNode{nodeap}, }, }, } { sort.Strings(c.have.Remove) sort.Sort(ByID(c.have.Add)) sort.Sort(ByID(c.have.Update)) if !reflect.DeepEqual(c.want, c.have) { t.Errorf("%s - %s", c.label, test.Diff(c.want, c.have)) } } }