github.com/handlerbot/terraform@v0.10.0-beta1.0.20180726153736-26b68d98f9cb/dag/marshal_test.go (about)

     1  package dag
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"strings"
     7  	"testing"
     8  )
     9  
    10  func TestGraphDot_empty(t *testing.T) {
    11  	var g Graph
    12  	g.Add(1)
    13  	g.Add(2)
    14  	g.Add(3)
    15  
    16  	actual := strings.TrimSpace(string(g.Dot(nil)))
    17  	expected := strings.TrimSpace(testGraphDotEmptyStr)
    18  	if actual != expected {
    19  		t.Fatalf("bad: %s", actual)
    20  	}
    21  }
    22  
    23  func TestGraphDot_basic(t *testing.T) {
    24  	var g Graph
    25  	g.Add(1)
    26  	g.Add(2)
    27  	g.Add(3)
    28  	g.Connect(BasicEdge(1, 3))
    29  
    30  	actual := strings.TrimSpace(string(g.Dot(nil)))
    31  	expected := strings.TrimSpace(testGraphDotBasicStr)
    32  	if actual != expected {
    33  		t.Fatalf("bad: %s", actual)
    34  	}
    35  }
    36  
    37  func TestGraphDot_attrs(t *testing.T) {
    38  	var g Graph
    39  	g.Add(&testGraphNodeDotter{
    40  		Result: &DotNode{
    41  			Name:  "foo",
    42  			Attrs: map[string]string{"foo": "bar"},
    43  		},
    44  	})
    45  
    46  	actual := strings.TrimSpace(string(g.Dot(nil)))
    47  	expected := strings.TrimSpace(testGraphDotAttrsStr)
    48  	if actual != expected {
    49  		t.Fatalf("bad: %s", actual)
    50  	}
    51  }
    52  
    53  type testGraphNodeDotter struct{ Result *DotNode }
    54  
    55  func (n *testGraphNodeDotter) Name() string                      { return n.Result.Name }
    56  func (n *testGraphNodeDotter) DotNode(string, *DotOpts) *DotNode { return n.Result }
    57  
    58  const testGraphDotBasicStr = `digraph {
    59  	compound = "true"
    60  	newrank = "true"
    61  	subgraph "root" {
    62  		"[root] 1" -> "[root] 3"
    63  	}
    64  }
    65  `
    66  
    67  const testGraphDotEmptyStr = `digraph {
    68  	compound = "true"
    69  	newrank = "true"
    70  	subgraph "root" {
    71  	}
    72  }`
    73  
    74  const testGraphDotAttrsStr = `digraph {
    75  	compound = "true"
    76  	newrank = "true"
    77  	subgraph "root" {
    78  		"[root] foo" [foo = "bar"]
    79  	}
    80  }`
    81  
    82  func TestGraphJSON_empty(t *testing.T) {
    83  	var g Graph
    84  	g.Add(1)
    85  	g.Add(2)
    86  	g.Add(3)
    87  
    88  	js, err := g.MarshalJSON()
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  
    93  	actual := strings.TrimSpace(string(js))
    94  	expected := strings.TrimSpace(testGraphJSONEmptyStr)
    95  	if actual != expected {
    96  		t.Fatalf("bad: %s", actual)
    97  	}
    98  }
    99  
   100  func TestGraphJSON_basic(t *testing.T) {
   101  	var g Graph
   102  	g.Add(1)
   103  	g.Add(2)
   104  	g.Add(3)
   105  	g.Connect(BasicEdge(1, 3))
   106  
   107  	js, err := g.MarshalJSON()
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  	actual := strings.TrimSpace(string(js))
   112  	expected := strings.TrimSpace(testGraphJSONBasicStr)
   113  	if actual != expected {
   114  		t.Fatalf("bad: %s", actual)
   115  	}
   116  }
   117  
   118  // record some graph transformations, and make sure we get the same graph when
   119  // they're replayed
   120  func TestGraphJSON_basicRecord(t *testing.T) {
   121  	var g Graph
   122  	var buf bytes.Buffer
   123  	g.SetDebugWriter(&buf)
   124  
   125  	g.Add(1)
   126  	g.Add(2)
   127  	g.Add(3)
   128  	g.Connect(BasicEdge(1, 2))
   129  	g.Connect(BasicEdge(1, 3))
   130  	g.Connect(BasicEdge(2, 3))
   131  	(&AcyclicGraph{g}).TransitiveReduction()
   132  
   133  	recorded := buf.Bytes()
   134  	// the Walk doesn't happen in a determined order, so just count operations
   135  	// for now to make sure we wrote stuff out.
   136  	if len(bytes.Split(recorded, []byte{'\n'})) != 17 {
   137  		t.Fatalf("bad: %s", recorded)
   138  	}
   139  
   140  	original, err := g.MarshalJSON()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	// replay the logs, and marshal the graph back out again
   146  	m, err := decodeGraph(bytes.NewReader(buf.Bytes()))
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	replayed, err := json.MarshalIndent(m, "", "  ")
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	if !bytes.Equal(original, replayed) {
   157  		t.Fatalf("\noriginal: %s\nreplayed: %s", original, replayed)
   158  	}
   159  }
   160  
   161  // Verify that Vertex and Edge annotations appear in the debug output
   162  func TestGraphJSON_debugInfo(t *testing.T) {
   163  	var g Graph
   164  	var buf bytes.Buffer
   165  	g.SetDebugWriter(&buf)
   166  
   167  	g.Add(1)
   168  	g.Add(2)
   169  	g.Add(3)
   170  	g.Connect(BasicEdge(1, 2))
   171  
   172  	g.DebugVertexInfo(2, "2")
   173  	g.DebugVertexInfo(3, "3")
   174  	g.DebugEdgeInfo(BasicEdge(1, 2), "1|2")
   175  
   176  	dec := json.NewDecoder(bytes.NewReader(buf.Bytes()))
   177  
   178  	var found2, found3, foundEdge bool
   179  	for dec.More() {
   180  		var d streamDecode
   181  
   182  		err := dec.Decode(&d)
   183  		if err != nil {
   184  			t.Fatal(err)
   185  		}
   186  
   187  		switch d.Type {
   188  		case typeVertexInfo:
   189  			va := &marshalVertexInfo{}
   190  			err := json.Unmarshal(d.JSON, va)
   191  			if err != nil {
   192  				t.Fatal(err)
   193  			}
   194  
   195  			switch va.Info {
   196  			case "2":
   197  				if va.Vertex.Name != "2" {
   198  					t.Fatalf("wrong vertex annotated 2: %#v", va)
   199  				}
   200  				found2 = true
   201  			case "3":
   202  				if va.Vertex.Name != "3" {
   203  					t.Fatalf("wrong vertex annotated 3: %#v", va)
   204  				}
   205  				found3 = true
   206  			default:
   207  				t.Fatalf("unexpected annotation: %#v", va)
   208  			}
   209  		case typeEdgeInfo:
   210  			ea := &marshalEdgeInfo{}
   211  			err := json.Unmarshal(d.JSON, ea)
   212  			if err != nil {
   213  				t.Fatal(err)
   214  			}
   215  
   216  			switch ea.Info {
   217  			case "1|2":
   218  				if ea.Edge.Name != "1|2" {
   219  					t.Fatalf("incorrect edge annotation: %#v\n", ea)
   220  				}
   221  				foundEdge = true
   222  			default:
   223  				t.Fatalf("unexpected edge Info: %#v", ea)
   224  			}
   225  		}
   226  	}
   227  
   228  	if !found2 {
   229  		t.Fatal("annotation 2 not found")
   230  	}
   231  	if !found3 {
   232  		t.Fatal("annotation 3 not found")
   233  	}
   234  	if !foundEdge {
   235  		t.Fatal("edge annotation not found")
   236  	}
   237  }
   238  
   239  // Verify that debug operations appear in the debug output
   240  func TestGraphJSON_debugOperations(t *testing.T) {
   241  	var g Graph
   242  	var buf bytes.Buffer
   243  	g.SetDebugWriter(&buf)
   244  
   245  	debugOp := g.DebugOperation("AddOne", "adding node 1")
   246  	g.Add(1)
   247  	debugOp.End("done adding node 1")
   248  
   249  	// use an immediate closure to test defers
   250  	func() {
   251  		defer g.DebugOperation("AddTwo", "adding nodes 2 and 3").End("done adding 2 and 3")
   252  		g.Add(2)
   253  		defer g.DebugOperation("NestedAddThree", "second defer").End("done adding node 3")
   254  		g.Add(3)
   255  	}()
   256  
   257  	g.Connect(BasicEdge(1, 2))
   258  
   259  	dec := json.NewDecoder(bytes.NewReader(buf.Bytes()))
   260  
   261  	var ops []string
   262  	for dec.More() {
   263  		var d streamDecode
   264  
   265  		err := dec.Decode(&d)
   266  		if err != nil {
   267  			t.Fatal(err)
   268  		}
   269  
   270  		if d.Type != typeOperation {
   271  			continue
   272  		}
   273  
   274  		o := &marshalOperation{}
   275  		err = json.Unmarshal(d.JSON, o)
   276  		if err != nil {
   277  			t.Fatal(err)
   278  		}
   279  
   280  		switch {
   281  		case o.Begin == "AddOne":
   282  			ops = append(ops, "BeginAddOne")
   283  		case o.End == "AddOne":
   284  			ops = append(ops, "EndAddOne")
   285  		case o.Begin == "AddTwo":
   286  			ops = append(ops, "BeginAddTwo")
   287  		case o.End == "AddTwo":
   288  			ops = append(ops, "EndAddTwo")
   289  		case o.Begin == "NestedAddThree":
   290  			ops = append(ops, "BeginAddThree")
   291  		case o.End == "NestedAddThree":
   292  			ops = append(ops, "EndAddThree")
   293  		}
   294  	}
   295  
   296  	expectedOps := []string{
   297  		"BeginAddOne",
   298  		"EndAddOne",
   299  		"BeginAddTwo",
   300  		"BeginAddThree",
   301  		"EndAddThree",
   302  		"EndAddTwo",
   303  	}
   304  
   305  	if strings.Join(ops, ",") != strings.Join(expectedOps, ",") {
   306  		t.Fatalf("incorrect order of operations: %v", ops)
   307  	}
   308  }
   309  
   310  // Verify that we can replay visiting each vertex in order
   311  func TestGraphJSON_debugVisits(t *testing.T) {
   312  	var g Graph
   313  	var buf bytes.Buffer
   314  	g.SetDebugWriter(&buf)
   315  
   316  	g.Add(1)
   317  	g.Add(2)
   318  	g.Add(3)
   319  	g.Add(4)
   320  
   321  	g.Connect(BasicEdge(2, 1))
   322  	g.Connect(BasicEdge(4, 2))
   323  	g.Connect(BasicEdge(3, 4))
   324  
   325  	err := (&AcyclicGraph{g}).Walk(func(v Vertex) error {
   326  		g.DebugVisitInfo(v, "basic walk")
   327  		return nil
   328  	})
   329  
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  
   334  	var visited []string
   335  
   336  	dec := json.NewDecoder(bytes.NewReader(buf.Bytes()))
   337  	for dec.More() {
   338  		var d streamDecode
   339  
   340  		err := dec.Decode(&d)
   341  		if err != nil {
   342  			t.Fatal(err)
   343  		}
   344  
   345  		if d.Type != typeVisitInfo {
   346  			continue
   347  		}
   348  
   349  		o := &marshalVertexInfo{}
   350  		err = json.Unmarshal(d.JSON, o)
   351  		if err != nil {
   352  			t.Fatal(err)
   353  		}
   354  
   355  		visited = append(visited, o.Vertex.ID)
   356  	}
   357  
   358  	expected := []string{"1", "2", "4", "3"}
   359  
   360  	if strings.Join(visited, "-") != strings.Join(expected, "-") {
   361  		t.Fatalf("incorrect order of operations: %v", visited)
   362  	}
   363  }
   364  
   365  const testGraphJSONEmptyStr = `{
   366    "Type": "Graph",
   367    "Name": "root",
   368    "Vertices": [
   369      {
   370        "ID": "1",
   371        "Name": "1"
   372      },
   373      {
   374        "ID": "2",
   375        "Name": "2"
   376      },
   377      {
   378        "ID": "3",
   379        "Name": "3"
   380      }
   381    ]
   382  }`
   383  
   384  const testGraphJSONBasicStr = `{
   385    "Type": "Graph",
   386    "Name": "root",
   387    "Vertices": [
   388      {
   389        "ID": "1",
   390        "Name": "1"
   391      },
   392      {
   393        "ID": "2",
   394        "Name": "2"
   395      },
   396      {
   397        "ID": "3",
   398        "Name": "3"
   399      }
   400    ],
   401    "Edges": [
   402      {
   403        "Name": "1|3",
   404        "Source": "1",
   405        "Target": "3"
   406      }
   407    ]
   408  }`