Brak opisu

dotgraph_test.go 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package graph
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io/ioutil"
  19. "reflect"
  20. "strconv"
  21. "strings"
  22. "testing"
  23. "github.com/google/pprof/internal/proftest"
  24. )
  25. const path = "testdata/"
  26. func TestComposeWithStandardGraph(t *testing.T) {
  27. g := baseGraph()
  28. a, c := baseAttrsAndConfig()
  29. var buf bytes.Buffer
  30. ComposeDot(&buf, g, a, c)
  31. want, err := ioutil.ReadFile(path + "compose1.dot")
  32. if err != nil {
  33. t.Fatalf("error reading test file: %v", err)
  34. }
  35. compareGraphs(t, buf.Bytes(), want)
  36. }
  37. func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
  38. g := baseGraph()
  39. a, c := baseAttrsAndConfig()
  40. // Set NodeAttributes for Node 1.
  41. a.Nodes[g.Nodes[0]] = &DotNodeAttributes{
  42. Shape: "folder",
  43. Bold: true,
  44. Peripheries: 2,
  45. URL: "www.google.com",
  46. Formatter: func(ni *NodeInfo) string {
  47. return strings.ToUpper(ni.Name)
  48. },
  49. }
  50. // Set Flat value to zero on Node 2.
  51. g.Nodes[1].Flat = 0
  52. var buf bytes.Buffer
  53. ComposeDot(&buf, g, a, c)
  54. want, err := ioutil.ReadFile(path + "compose2.dot")
  55. if err != nil {
  56. t.Fatalf("error reading test file: %v", err)
  57. }
  58. compareGraphs(t, buf.Bytes(), want)
  59. }
  60. func TestComposeWithTagsAndResidualEdge(t *testing.T) {
  61. g := baseGraph()
  62. a, c := baseAttrsAndConfig()
  63. // Add tags to Node 1.
  64. g.Nodes[0].LabelTags["a"] = &Tag{
  65. Name: "tag1",
  66. Cum: 10,
  67. Flat: 10,
  68. }
  69. g.Nodes[0].NumericTags[""] = TagMap{
  70. "b": &Tag{
  71. Name: "tag2",
  72. Cum: 20,
  73. Flat: 20,
  74. Unit: "ms",
  75. },
  76. }
  77. // Set edge to be Residual.
  78. g.Nodes[0].Out[g.Nodes[1]].Residual = true
  79. var buf bytes.Buffer
  80. ComposeDot(&buf, g, a, c)
  81. want, err := ioutil.ReadFile(path + "compose3.dot")
  82. if err != nil {
  83. t.Fatalf("error reading test file: %v", err)
  84. }
  85. compareGraphs(t, buf.Bytes(), want)
  86. }
  87. func TestComposeWithNestedTags(t *testing.T) {
  88. g := baseGraph()
  89. a, c := baseAttrsAndConfig()
  90. // Add tags to Node 1.
  91. g.Nodes[0].LabelTags["tag1"] = &Tag{
  92. Name: "tag1",
  93. Cum: 10,
  94. Flat: 10,
  95. }
  96. g.Nodes[0].NumericTags["tag1"] = TagMap{
  97. "tag2": &Tag{
  98. Name: "tag2",
  99. Cum: 20,
  100. Flat: 20,
  101. Unit: "ms",
  102. },
  103. }
  104. var buf bytes.Buffer
  105. ComposeDot(&buf, g, a, c)
  106. want, err := ioutil.ReadFile(path + "compose5.dot")
  107. if err != nil {
  108. t.Fatalf("error reading test file: %v", err)
  109. }
  110. compareGraphs(t, buf.Bytes(), want)
  111. }
  112. func TestComposeWithEmptyGraph(t *testing.T) {
  113. g := &Graph{}
  114. a, c := baseAttrsAndConfig()
  115. var buf bytes.Buffer
  116. ComposeDot(&buf, g, a, c)
  117. want, err := ioutil.ReadFile(path + "compose4.dot")
  118. if err != nil {
  119. t.Fatalf("error reading test file: %v", err)
  120. }
  121. compareGraphs(t, buf.Bytes(), want)
  122. }
  123. func TestComposeWithStandardGraphAndURL(t *testing.T) {
  124. g := baseGraph()
  125. a, c := baseAttrsAndConfig()
  126. c.LegendURL = "http://example.com"
  127. var buf bytes.Buffer
  128. ComposeDot(&buf, g, a, c)
  129. want, err := ioutil.ReadFile(path + "compose6.dot")
  130. if err != nil {
  131. t.Fatalf("error reading test file: %v", err)
  132. }
  133. compareGraphs(t, buf.Bytes(), want)
  134. }
  135. func baseGraph() *Graph {
  136. src := &Node{
  137. Info: NodeInfo{Name: "src"},
  138. Flat: 10,
  139. Cum: 25,
  140. In: make(EdgeMap),
  141. Out: make(EdgeMap),
  142. LabelTags: make(TagMap),
  143. NumericTags: make(map[string]TagMap),
  144. }
  145. dest := &Node{
  146. Info: NodeInfo{Name: "dest"},
  147. Flat: 15,
  148. Cum: 25,
  149. In: make(EdgeMap),
  150. Out: make(EdgeMap),
  151. LabelTags: make(TagMap),
  152. NumericTags: make(map[string]TagMap),
  153. }
  154. edge := &Edge{
  155. Src: src,
  156. Dest: dest,
  157. Weight: 10,
  158. }
  159. src.Out[dest] = edge
  160. src.In[src] = edge
  161. return &Graph{
  162. Nodes: Nodes{
  163. src,
  164. dest,
  165. },
  166. }
  167. }
  168. func baseAttrsAndConfig() (*DotAttributes, *DotConfig) {
  169. a := &DotAttributes{
  170. Nodes: make(map[*Node]*DotNodeAttributes),
  171. }
  172. c := &DotConfig{
  173. Title: "testtitle",
  174. Labels: []string{"label1", "label2"},
  175. Total: 100,
  176. FormatValue: func(v int64) string {
  177. return strconv.FormatInt(v, 10)
  178. },
  179. }
  180. return a, c
  181. }
  182. func compareGraphs(t *testing.T, got, want []byte) {
  183. if string(got) != string(want) {
  184. d, err := proftest.Diff(got, want)
  185. if err != nil {
  186. t.Fatalf("error finding diff: %v", err)
  187. }
  188. t.Errorf("Compose incorrectly wrote %s", string(d))
  189. }
  190. }
  191. func TestNodeletCountCapping(t *testing.T) {
  192. labelTags := make(TagMap)
  193. for i := 0; i < 10; i++ {
  194. name := fmt.Sprintf("tag-%d", i)
  195. labelTags[name] = &Tag{
  196. Name: name,
  197. Flat: 10,
  198. Cum: 10,
  199. }
  200. }
  201. numTags := make(TagMap)
  202. for i := 0; i < 10; i++ {
  203. name := fmt.Sprintf("num-tag-%d", i)
  204. numTags[name] = &Tag{
  205. Name: name,
  206. Unit: "mb",
  207. Value: 16,
  208. Flat: 10,
  209. Cum: 10,
  210. }
  211. }
  212. node1 := &Node{
  213. Info: NodeInfo{Name: "node1-with-tags"},
  214. Flat: 10,
  215. Cum: 10,
  216. NumericTags: map[string]TagMap{"": numTags},
  217. LabelTags: labelTags,
  218. }
  219. node2 := &Node{
  220. Info: NodeInfo{Name: "node2"},
  221. Flat: 15,
  222. Cum: 15,
  223. }
  224. node3 := &Node{
  225. Info: NodeInfo{Name: "node3"},
  226. Flat: 15,
  227. Cum: 15,
  228. }
  229. g := &Graph{
  230. Nodes: Nodes{
  231. node1,
  232. node2,
  233. node3,
  234. },
  235. }
  236. for n := 1; n <= 3; n++ {
  237. input := maxNodelets + n
  238. if got, want := len(g.SelectTopNodes(input, true)), n; got != want {
  239. t.Errorf("SelectTopNodes(%d): got %d nodes, want %d", input, got, want)
  240. }
  241. }
  242. }
  243. func TestMultilinePrintableName(t *testing.T) {
  244. ni := &NodeInfo{
  245. Name: "test1.test2::test3",
  246. File: "src/file.cc",
  247. Address: 123,
  248. Lineno: 999,
  249. }
  250. want := fmt.Sprintf(`%016x\ntest1\ntest2\ntest3\nfile.cc:999\n`, 123)
  251. if got := multilinePrintableName(ni); got != want {
  252. t.Errorf("multilinePrintableName(%#v) == %q, want %q", ni, got, want)
  253. }
  254. }
  255. func TestTagCollapse(t *testing.T) {
  256. makeTag := func(name, unit string, value, flat, cum int64) *Tag {
  257. return &Tag{name, unit, value, flat, 0, cum, 0}
  258. }
  259. tagSource := []*Tag{
  260. makeTag("12mb", "mb", 12, 100, 100),
  261. makeTag("1kb", "kb", 1, 1, 1),
  262. makeTag("1mb", "mb", 1, 1000, 1000),
  263. makeTag("2048mb", "mb", 2048, 1000, 1000),
  264. makeTag("1b", "b", 1, 100, 100),
  265. makeTag("2b", "b", 2, 100, 100),
  266. makeTag("7b", "b", 7, 100, 100),
  267. }
  268. tagWant := [][]*Tag{
  269. {
  270. makeTag("1B..2GB", "", 0, 2401, 2401),
  271. },
  272. {
  273. makeTag("2GB", "", 0, 1000, 1000),
  274. makeTag("1B..12MB", "", 0, 1401, 1401),
  275. },
  276. {
  277. makeTag("2GB", "", 0, 1000, 1000),
  278. makeTag("12MB", "", 0, 100, 100),
  279. makeTag("1B..1MB", "", 0, 1301, 1301),
  280. },
  281. {
  282. makeTag("2GB", "", 0, 1000, 1000),
  283. makeTag("1MB", "", 0, 1000, 1000),
  284. makeTag("2B..1kB", "", 0, 201, 201),
  285. makeTag("1B", "", 0, 100, 100),
  286. makeTag("12MB", "", 0, 100, 100),
  287. },
  288. }
  289. for _, tc := range tagWant {
  290. var got, want []*Tag
  291. b := builder{nil, &DotAttributes{}, &DotConfig{}}
  292. got = b.collapsedTags(tagSource, len(tc), true)
  293. want = SortTags(tc, true)
  294. if !reflect.DeepEqual(got, want) {
  295. t.Errorf("collapse to %d, got:\n%v\nwant:\n%v", len(tc), tagString(got), tagString(want))
  296. }
  297. }
  298. }
  299. func tagString(t []*Tag) string {
  300. var ret []string
  301. for _, s := range t {
  302. ret = append(ret, fmt.Sprintln(s))
  303. }
  304. return strings.Join(ret, ":")
  305. }