node_test.go 3.69 KB
Newer Older
zhangweiwei's avatar
init  
zhangweiwei committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package html

import (
	"fmt"
)

// checkTreeConsistency checks that a node and its descendants are all
// consistent in their parent/child/sibling relationships.
func checkTreeConsistency(n *Node) error {
	return checkTreeConsistency1(n, 0)
}

func checkTreeConsistency1(n *Node, depth int) error {
	if depth == 1e4 {
		return fmt.Errorf("html: tree looks like it contains a cycle")
	}
	if err := checkNodeConsistency(n); err != nil {
		return err
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if err := checkTreeConsistency1(c, depth+1); err != nil {
			return err
		}
	}
	return nil
}

// checkNodeConsistency checks that a node's parent/child/sibling relationships
// are consistent.
func checkNodeConsistency(n *Node) error {
	if n == nil {
		return nil
	}

	nParent := 0
	for p := n.Parent; p != nil; p = p.Parent {
		nParent++
		if nParent == 1e4 {
			return fmt.Errorf("html: parent list looks like an infinite loop")
		}
	}

	nForward := 0
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		nForward++
		if nForward == 1e6 {
			return fmt.Errorf("html: forward list of children looks like an infinite loop")
		}
		if c.Parent != n {
			return fmt.Errorf("html: inconsistent child/parent relationship")
		}
	}

	nBackward := 0
	for c := n.LastChild; c != nil; c = c.PrevSibling {
		nBackward++
		if nBackward == 1e6 {
			return fmt.Errorf("html: backward list of children looks like an infinite loop")
		}
		if c.Parent != n {
			return fmt.Errorf("html: inconsistent child/parent relationship")
		}
	}

	if n.Parent != nil {
		if n.Parent == n {
			return fmt.Errorf("html: inconsistent parent relationship")
		}
		if n.Parent == n.FirstChild {
			return fmt.Errorf("html: inconsistent parent/first relationship")
		}
		if n.Parent == n.LastChild {
			return fmt.Errorf("html: inconsistent parent/last relationship")
		}
		if n.Parent == n.PrevSibling {
			return fmt.Errorf("html: inconsistent parent/prev relationship")
		}
		if n.Parent == n.NextSibling {
			return fmt.Errorf("html: inconsistent parent/next relationship")
		}

		parentHasNAsAChild := false
		for c := n.Parent.FirstChild; c != nil; c = c.NextSibling {
			if c == n {
				parentHasNAsAChild = true
				break
			}
		}
		if !parentHasNAsAChild {
			return fmt.Errorf("html: inconsistent parent/child relationship")
		}
	}

	if n.PrevSibling != nil && n.PrevSibling.NextSibling != n {
		return fmt.Errorf("html: inconsistent prev/next relationship")
	}
	if n.NextSibling != nil && n.NextSibling.PrevSibling != n {
		return fmt.Errorf("html: inconsistent next/prev relationship")
	}

	if (n.FirstChild == nil) != (n.LastChild == nil) {
		return fmt.Errorf("html: inconsistent first/last relationship")
	}
	if n.FirstChild != nil && n.FirstChild == n.LastChild {
		// We have a sole child.
		if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil {
			return fmt.Errorf("html: inconsistent sole child's sibling relationship")
		}
	}

	seen := map[*Node]bool{}

	var last *Node
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if seen[c] {
			return fmt.Errorf("html: inconsistent repeated child")
		}
		seen[c] = true
		last = c
	}
	if last != n.LastChild {
		return fmt.Errorf("html: inconsistent last relationship")
	}

	var first *Node
	for c := n.LastChild; c != nil; c = c.PrevSibling {
		if !seen[c] {
			return fmt.Errorf("html: inconsistent missing child")
		}
		delete(seen, c)
		first = c
	}
	if first != n.FirstChild {
		return fmt.Errorf("html: inconsistent first relationship")
	}

	if len(seen) != 0 {
		return fmt.Errorf("html: inconsistent forwards/backwards child list")
	}

	return nil
}