github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/node_cache_test.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"runtime"
     9  	"testing"
    10  
    11  	"github.com/keybase/client/go/kbfs/data"
    12  	"github.com/keybase/client/go/kbfs/kbfsblock"
    13  	"github.com/keybase/client/go/kbfs/tlf"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func setupNodeCache(t *testing.T, id tlf.ID, branch data.BranchName, flat bool) (
    18  	ncs *nodeCacheStandard, parentNode Node, childNode1 Node, childNode2 Node,
    19  	childPath1 []data.PathNode, childPath2 []data.PathNode) {
    20  	ncs = newNodeCacheStandard(data.FolderBranch{Tlf: id, Branch: branch})
    21  
    22  	parentPtr := data.BlockPointer{ID: kbfsblock.FakeID(0)}
    23  	parentName := "parent"
    24  	var err error
    25  	parentNode, err = ncs.GetOrCreate(
    26  		parentPtr, testPPS(parentName), nil, data.Dir)
    27  	if err != nil {
    28  		t.Errorf("Couldn't create top-level parent node: %v", err)
    29  	}
    30  	if parentNode.GetBasename().Plaintext() != parentName {
    31  		t.Errorf("Expected basename %s, got %s",
    32  			parentName, parentNode.GetBasename())
    33  	}
    34  
    35  	// now create a child node for that parent
    36  	childPtr1 := data.BlockPointer{ID: kbfsblock.FakeID(1)}
    37  	childName1 := "child1"
    38  	childNode1, err = ncs.GetOrCreate(
    39  		childPtr1, testPPS(childName1), parentNode, data.Dir)
    40  	if err != nil {
    41  		t.Errorf("Couldn't create child node: %v", err)
    42  	}
    43  	if childNode1.GetBasename().Plaintext() != childName1 {
    44  		t.Errorf("Expected basename %s, got %s",
    45  			childName1, childNode1.GetBasename())
    46  	}
    47  
    48  	parent2 := childNode1
    49  	if flat {
    50  		parent2 = parentNode
    51  	}
    52  
    53  	childPtr2 := data.BlockPointer{ID: kbfsblock.FakeID(2)}
    54  	childName2 := "child2"
    55  	childNode2, err = ncs.GetOrCreate(
    56  		childPtr2, testPPS(childName2), parent2, data.Dir)
    57  	if err != nil {
    58  		t.Errorf("Couldn't create second child node: %v", err)
    59  	}
    60  	if childNode2.GetBasename().Plaintext() != childName2 {
    61  		t.Errorf("Expected basename %s, got %s",
    62  			childName2, childNode2.GetBasename())
    63  	}
    64  
    65  	childPath1 = []data.PathNode{
    66  		{
    67  			BlockPointer: parentPtr,
    68  			Name:         testPPS(parentName),
    69  		},
    70  		{
    71  			BlockPointer: childPtr1,
    72  			Name:         testPPS(childName1),
    73  		},
    74  	}
    75  	if flat {
    76  		childPath2 = []data.PathNode{
    77  			{
    78  				BlockPointer: parentPtr,
    79  				Name:         testPPS(parentName),
    80  			},
    81  			{
    82  				BlockPointer: childPtr2,
    83  				Name:         testPPS(childName2),
    84  			},
    85  		}
    86  	} else {
    87  		childPath2 = []data.PathNode{
    88  			{
    89  				BlockPointer: parentPtr,
    90  				Name:         testPPS(parentName),
    91  			},
    92  			{
    93  				BlockPointer: childPtr1,
    94  				Name:         testPPS(childName1),
    95  			},
    96  			{
    97  				BlockPointer: childPtr2,
    98  				Name:         testPPS(childName2),
    99  			},
   100  		}
   101  	}
   102  	return
   103  }
   104  
   105  // Simulate a GC cycle where all the nodes in liveList still have
   106  // references.
   107  //
   108  // (Doing real GC cycles and running finalizers, etc. is brittle.)
   109  func simulateGC(ncs *nodeCacheStandard, liveList []Node) {
   110  	hasWork := true
   111  	for hasWork {
   112  		hasWork = false
   113  
   114  		liveSet := make(map[*nodeCore]bool)
   115  
   116  		// Everything in liveList is live.
   117  		for _, n := range liveList {
   118  			liveSet[n.(*nodeStandard).core] = true
   119  		}
   120  
   121  		// Everything referenced as a parent is live.
   122  		for _, e := range ncs.nodes {
   123  			if e.core.parent != nil {
   124  				p := e.core.parent.Unwrap().(*nodeStandard)
   125  				liveSet[p.core] = true
   126  			}
   127  		}
   128  
   129  		// Forget everything not live.
   130  		for _, e := range ncs.nodes {
   131  			if _, ok := liveSet[e.core]; !ok {
   132  				ncs.forget(e.core)
   133  				hasWork = true
   134  			}
   135  		}
   136  	}
   137  }
   138  
   139  // Tests for simple GetOrCreate successes (with and without a parent)
   140  func TestNodeCacheGetOrCreateSuccess(t *testing.T) {
   141  	ncs, parentNode, childNode1A, _, path1, path2 :=
   142  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   143  	parentPtr := path1[0].BlockPointer
   144  	childPtr1 := path1[1].BlockPointer
   145  	childPtr2 := path2[1].BlockPointer
   146  
   147  	// make sure we get the same node back for the second call
   148  	childNode1B, err := ncs.GetOrCreate(
   149  		childPtr1, childNode1A.GetBasename(), parentNode, data.Dir)
   150  	if err != nil {
   151  		t.Errorf("Couldn't create child node: %v", err)
   152  	}
   153  	if childNode1A.(*nodeStandard).core != childNode1B.(*nodeStandard).core {
   154  		t.Error("Two creates for the same child!")
   155  	}
   156  
   157  	// now make sure the refCounts are right.
   158  	if ncs.nodes[parentPtr.Ref()].refCount != 1 {
   159  		t.Errorf("Parent has wrong refcount: %d", ncs.nodes[parentPtr.Ref()].refCount)
   160  	}
   161  	if ncs.nodes[childPtr1.Ref()].refCount != 2 {
   162  		t.Errorf("Child1 has wrong refcount: %d", ncs.nodes[childPtr1.Ref()].refCount)
   163  	}
   164  	if ncs.nodes[childPtr2.Ref()].refCount != 1 {
   165  		t.Errorf("Child1 has wrong refcount: %d", ncs.nodes[childPtr2.Ref()].refCount)
   166  	}
   167  }
   168  
   169  // Tests that a child can't be created with an unknown parent.
   170  func TestNodeCacheGetOrCreateNoParent(t *testing.T) {
   171  	ncs := newNodeCacheStandard(data.FolderBranch{
   172  		Tlf:    tlf.FakeID(0, tlf.Private),
   173  		Branch: "",
   174  	})
   175  
   176  	parentPtr := data.BlockPointer{ID: kbfsblock.FakeID(0)}
   177  	parentNode, err := ncs.GetOrCreate(
   178  		parentPtr, testPPS("parent"), nil, data.Dir)
   179  	if err != nil {
   180  		t.Errorf("Couldn't create top-level parent node: %v", err)
   181  	}
   182  
   183  	simulateGC(ncs, []Node{})
   184  
   185  	// now try to create a child node for that parent
   186  	childPtr1 := data.BlockPointer{ID: kbfsblock.FakeID(1)}
   187  	_, err = ncs.GetOrCreate(childPtr1, testPPS("child"), parentNode, data.Dir)
   188  	expectedErr := ParentNodeNotFoundError{parentPtr.Ref()}
   189  	if err != expectedErr {
   190  		t.Errorf("Got unexpected error when creating w/o parent: %v", err)
   191  	}
   192  }
   193  
   194  // Tests that UpdatePointer works
   195  func TestNodeCacheUpdatePointer(t *testing.T) {
   196  	ncs := newNodeCacheStandard(data.FolderBranch{
   197  		Tlf:    tlf.FakeID(0, tlf.Private),
   198  		Branch: "",
   199  	})
   200  
   201  	parentPtr := data.BlockPointer{ID: kbfsblock.FakeID(0)}
   202  	parentNode, err := ncs.GetOrCreate(
   203  		parentPtr, testPPS("parent"), nil, data.Dir)
   204  	if err != nil {
   205  		t.Errorf("Couldn't create top-level parent node: %v", err)
   206  	}
   207  
   208  	newParentPtr := data.BlockPointer{ID: kbfsblock.FakeID(1)}
   209  	ncs.UpdatePointer(parentPtr.Ref(), newParentPtr)
   210  
   211  	if parentNode.(*nodeStandard).core.pathNode.BlockPointer != newParentPtr {
   212  		t.Errorf("UpdatePointer didn't work.")
   213  	}
   214  }
   215  
   216  // Tests that Move works as expected
   217  func TestNodeCacheMoveSuccess(t *testing.T) {
   218  	ncs, parentNode, childNode1, childNode2, path1, path2 :=
   219  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   220  	parentPtr := path1[0].BlockPointer
   221  	childPtr1 := path1[1].BlockPointer
   222  	childPtr2 := path2[1].BlockPointer
   223  
   224  	// now move child2 under child1
   225  	undoMove, err := ncs.Move(childPtr2.Ref(), childNode1, testPPS("child3"))
   226  	if err != nil {
   227  		t.Errorf("Couldn't update parent: %v", err)
   228  	}
   229  
   230  	if childNode2.GetBasename().Plaintext() != "child3" {
   231  		t.Errorf("Child2 has the wrong name after move: %s",
   232  			childNode2.GetBasename())
   233  	}
   234  
   235  	if childNode2.(*nodeStandard).core.parent != childNode1 {
   236  		t.Errorf("UpdateParent didn't work")
   237  	}
   238  
   239  	// now make sure all nodes have 1 reference.
   240  	if ncs.nodes[parentPtr.Ref()].refCount != 1 {
   241  		t.Errorf("Parent has wrong refcount: %d", ncs.nodes[parentPtr.Ref()].refCount)
   242  	}
   243  	if ncs.nodes[childPtr1.Ref()].refCount != 1 {
   244  		t.Errorf("Child1 has wrong refcount: %d", ncs.nodes[childPtr1.Ref()].refCount)
   245  	}
   246  	if ncs.nodes[childPtr2.Ref()].refCount != 1 {
   247  		t.Errorf("Child1 has wrong refcount: %d", ncs.nodes[childPtr2.Ref()].refCount)
   248  	}
   249  
   250  	undoMove()
   251  	if childNode2.GetBasename().Plaintext() != "child2" {
   252  		t.Errorf("Child2 has the wrong name after move: %s",
   253  			childNode2.GetBasename())
   254  	}
   255  	if childNode2.(*nodeStandard).core.parent != parentNode {
   256  		t.Errorf("UpdateParent didn't work")
   257  	}
   258  
   259  }
   260  
   261  // Tests that a child can't be updated with an unknown parent
   262  func TestNodeCacheMoveNoParent(t *testing.T) {
   263  	ncs, _, childNode1, childNode2, path1, path2 :=
   264  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   265  	childPtr1 := path1[1].BlockPointer
   266  	childPtr2 := path2[1].BlockPointer
   267  
   268  	// get rid of child1
   269  	simulateGC(ncs, []Node{childNode2})
   270  
   271  	// now move child2 under child1
   272  	_, err := ncs.Move(childPtr2.Ref(), childNode1, testPPS("child3"))
   273  	expectedErr := ParentNodeNotFoundError{childPtr1.Ref()}
   274  	if err != expectedErr {
   275  		t.Errorf("Got unexpected error when updating parent: %v", err)
   276  	}
   277  }
   278  
   279  func checkNodeCachePath(t *testing.T, id tlf.ID, branch data.BranchName,
   280  	path data.Path, expectedPath []data.PathNode) {
   281  	if len(path.Path) != len(expectedPath) {
   282  		t.Errorf("Bad path length: %v vs %v", len(path.Path), len(expectedPath))
   283  	}
   284  
   285  	for i, n := range expectedPath {
   286  		if path.Path[i] != n {
   287  			t.Errorf("Bad node on path, index %d: %v vs %v", i, path.Path[i], n)
   288  		}
   289  	}
   290  	if path.Tlf != id {
   291  		t.Errorf("Wrong top dir: %v vs %v", path.Tlf, id)
   292  	}
   293  	if path.Branch != branch {
   294  		t.Errorf("Wrong branch: %s vs %s", path.Branch, branch)
   295  	}
   296  }
   297  
   298  // Tests that a child can be unlinked completely from the parent, and
   299  // still have a path, but not a basename.
   300  func TestNodeCacheUnlink(t *testing.T) {
   301  	id := tlf.FakeID(42, tlf.Private)
   302  	branch := data.BranchName("testBranch")
   303  	ncs, _, _, childNode2, _, path2 :=
   304  		setupNodeCache(t, id, branch, false)
   305  	childPtr2 := path2[2].BlockPointer
   306  
   307  	// unlink child2
   308  	undoFn := ncs.Unlink(
   309  		childPtr2.Ref(), ncs.PathFromNode(childNode2), data.DirEntry{})
   310  	if undoFn == nil {
   311  		t.Fatalf("Couldn't unlink")
   312  	}
   313  
   314  	path := ncs.PathFromNode(childNode2)
   315  	checkNodeCachePath(t, id, branch, path, path2)
   316  
   317  	if childNode2.GetBasename().Plaintext() != "" {
   318  		t.Errorf("Expected empty basename, got %s", childNode2.GetBasename())
   319  	}
   320  
   321  	// Undo
   322  	undoFn()
   323  	if childNode2.GetBasename().Plaintext() != path2[2].Name.Plaintext() {
   324  		t.Errorf("Expected basename %s, got %s",
   325  			path2[2].Name, childNode2.GetBasename())
   326  	}
   327  }
   328  
   329  // Tests that a child's ancestor can be unlinked completely from its
   330  // parent, and the child still has a path and a basename.
   331  func TestNodeCacheUnlinkParent(t *testing.T) {
   332  	id := tlf.FakeID(42, tlf.Private)
   333  	branch := data.BranchName("testBranch")
   334  	ncs, _, childNode1, childNode2, _, path2 :=
   335  		setupNodeCache(t, id, branch, false)
   336  	childPtr1 := path2[1].BlockPointer
   337  
   338  	// unlink node 2's parent
   339  	undoFn := ncs.Unlink(
   340  		childPtr1.Ref(), ncs.PathFromNode(childNode1), data.DirEntry{})
   341  	if undoFn == nil {
   342  		t.Fatalf("Couldn't unlink")
   343  	}
   344  
   345  	path := ncs.PathFromNode(childNode2)
   346  	checkNodeCachePath(t, id, branch, path, path2)
   347  
   348  	if childNode2.GetBasename().Plaintext() != "child2" {
   349  		t.Errorf("Expected basename child2, got %s", childNode2.GetBasename())
   350  	}
   351  }
   352  
   353  // Tests that a child can be unlinked completely from the parent, and
   354  // then re-added with a new pointer and still work, but with a new
   355  // node core.
   356  func TestNodeCacheUnlinkThenRelink(t *testing.T) {
   357  	id := tlf.FakeID(42, tlf.Private)
   358  	branch := data.BranchName("testBranch")
   359  	ncs, _, childNode1, childNode2, _, path2 :=
   360  		setupNodeCache(t, id, branch, false)
   361  	childPtr2 := path2[2].BlockPointer
   362  
   363  	// unlink child2
   364  	undoFn := ncs.Unlink(
   365  		childPtr2.Ref(), ncs.PathFromNode(childNode2), data.DirEntry{})
   366  	if undoFn == nil {
   367  		t.Fatalf("Couldn't unlink")
   368  	}
   369  
   370  	newChildName := "newChildName"
   371  	newChildPtr2 := data.BlockPointer{ID: kbfsblock.FakeID(22)}
   372  	ncs.UpdatePointer(childPtr2.Ref(), newChildPtr2) // NO-OP
   373  	childNode2B, err := ncs.GetOrCreate(
   374  		newChildPtr2, testPPS(newChildName), childNode1, data.Dir)
   375  	if err != nil {
   376  		t.Fatalf("Couldn't relink node: %v", err)
   377  	}
   378  	if childNode2.GetID() == childNode2B.GetID() {
   379  		t.Errorf("Relink left the node the same")
   380  	}
   381  
   382  	// Old unlinked node didn't get updated
   383  	path := ncs.PathFromNode(childNode2)
   384  	checkNodeCachePath(t, id, branch, path, path2)
   385  
   386  	// New node
   387  	path = ncs.PathFromNode(childNode2B)
   388  	path2[2].BlockPointer = newChildPtr2
   389  	path2[2].Name = testPPS(newChildName)
   390  	checkNodeCachePath(t, id, branch, path, path2)
   391  
   392  	if g, e := childNode2.GetBasename().Plaintext(), ""; g != e {
   393  		t.Errorf("Expected basename %s, got %s", e, g)
   394  	}
   395  	if g, e := childNode2B.GetBasename().Plaintext(), newChildName; g != e {
   396  		t.Errorf("Expected basename %s, got %s", e, g)
   397  	}
   398  }
   399  
   400  // Tests that PathFromNode works correctly
   401  func TestNodeCachePathFromNode(t *testing.T) {
   402  	id := tlf.FakeID(42, tlf.Private)
   403  	branch := data.BranchName("testBranch")
   404  	ncs, _, _, childNode2, _, path2 :=
   405  		setupNodeCache(t, id, branch, false)
   406  	path := ncs.PathFromNode(childNode2)
   407  	checkNodeCachePath(t, id, branch, path, path2)
   408  }
   409  
   410  // Make sure that (simulated) GC works as expected.
   411  func TestNodeCacheGCBasic(t *testing.T) {
   412  	ncs, parentNode, _, childNode2, _, _ :=
   413  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   414  
   415  	if len(ncs.nodes) != 3 {
   416  		t.Errorf("Expected %d nodes, got %d", 3, len(ncs.nodes))
   417  	}
   418  
   419  	simulateGC(ncs, []Node{parentNode, childNode2})
   420  
   421  	if len(ncs.nodes) != 2 {
   422  		t.Errorf("Expected %d nodes, got %d", 2, len(ncs.nodes))
   423  	}
   424  
   425  	simulateGC(ncs, []Node{parentNode})
   426  
   427  	if len(ncs.nodes) != 1 {
   428  		t.Errorf("Expected %d nodes, got %d", 1, len(ncs.nodes))
   429  	}
   430  
   431  	simulateGC(ncs, []Node{})
   432  
   433  	if len(ncs.nodes) != 0 {
   434  		t.Errorf("Expected %d nodes, got %d", 0, len(ncs.nodes))
   435  	}
   436  }
   437  
   438  // Make sure that GC works as expected when a child node holds the
   439  // last reference to a parent.
   440  func TestNodeCacheGCParent(t *testing.T) {
   441  	ncs, _, _, childNode2, _, _ :=
   442  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   443  
   444  	if len(ncs.nodes) != 3 {
   445  		t.Errorf("Expected %d nodes, got %d", 3, len(ncs.nodes))
   446  	}
   447  
   448  	simulateGC(ncs, []Node{childNode2})
   449  
   450  	if len(ncs.nodes) != 2 {
   451  		t.Errorf("Expected %d nodes, got %d", 2, len(ncs.nodes))
   452  	}
   453  
   454  	simulateGC(ncs, []Node{})
   455  
   456  	if len(ncs.nodes) != 0 {
   457  		t.Errorf("Expected %d nodes, got %d", 0, len(ncs.nodes))
   458  	}
   459  }
   460  
   461  var finalizerChan = make(chan struct{})
   462  
   463  // Like nodeStandardFinalizer(), but sends on finalizerChan
   464  // afterwards.
   465  func testNodeStandardFinalizer(n *nodeStandard) {
   466  	nodeStandardFinalizer(n)
   467  	finalizerChan <- struct{}{}
   468  }
   469  
   470  // Make sure that that making a node unreachable runs the finalizer on GC.
   471  func TestNodeCacheGCReal(t *testing.T) {
   472  	ncs, _, childNode1, childNode2, _, _ :=
   473  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, true)
   474  
   475  	if len(ncs.nodes) != 3 {
   476  		t.Errorf("Expected %d nodes, got %d", 3, len(ncs.nodes))
   477  	}
   478  
   479  	runtime.SetFinalizer(childNode1, nil)
   480  	runtime.SetFinalizer(childNode1, testNodeStandardFinalizer)
   481  
   482  	childNode1 = nil // nolint
   483  	runtime.GC()
   484  	<-finalizerChan
   485  
   486  	require.Len(t, ncs.nodes, 2)
   487  
   488  	// Make sure childNode2 isn't GCed until after this point.
   489  	runtime.KeepAlive(childNode2)
   490  }
   491  
   492  type wrappedTestNode struct {
   493  	Node
   494  	wrapChildCalled bool
   495  }
   496  
   497  func (wtn *wrappedTestNode) WrapChild(child Node) Node {
   498  	child = wtn.Node.WrapChild(child)
   499  	wtn.wrapChildCalled = true
   500  	return child
   501  }
   502  
   503  func TestNodeCacheWrapChild(t *testing.T) {
   504  	ncs := newNodeCacheStandard(
   505  		data.FolderBranch{
   506  			Tlf:    tlf.FakeID(0, tlf.Private),
   507  			Branch: data.MasterBranch,
   508  		})
   509  	var wtn1, wtn2 *wrappedTestNode
   510  	rw1 := func(root Node) Node {
   511  		wtn1 = &wrappedTestNode{root, false}
   512  		return wtn1
   513  	}
   514  	rw2 := func(root Node) Node {
   515  		wtn2 = &wrappedTestNode{root, false}
   516  		return wtn2
   517  	}
   518  	ncs.AddRootWrapper(rw1)
   519  	ncs.AddRootWrapper(rw2)
   520  
   521  	rootPtr := data.BlockPointer{ID: kbfsblock.FakeID(0)}
   522  	rootName := "root"
   523  	rootNode, err := ncs.GetOrCreate(rootPtr, testPPS(rootName), nil, data.Dir)
   524  	require.NoError(t, err)
   525  
   526  	childPtr := data.BlockPointer{ID: kbfsblock.FakeID(1)}
   527  	childName := "child1"
   528  	_, err = ncs.GetOrCreate(childPtr, testPPS(childName), rootNode, data.Dir)
   529  	require.NoError(t, err)
   530  	require.True(t, wtn1.wrapChildCalled)
   531  	require.True(t, wtn2.wrapChildCalled)
   532  }
   533  
   534  func TestNodeCacheAllNodeChildren(t *testing.T) {
   535  	ncs, parentNode, childNode1, childNode2, _, _ :=
   536  		setupNodeCache(t, tlf.FakeID(0, tlf.Private), data.MasterBranch, false)
   537  
   538  	// Structure:
   539  	// parent:
   540  	//   child1:
   541  	//      child2
   542  	//      child3
   543  	//   child4
   544  
   545  	childPtr3 := data.BlockPointer{ID: kbfsblock.FakeID(3)}
   546  	childName3 := "child3"
   547  	_, err := ncs.GetOrCreate(
   548  		childPtr3, testPPS(childName3), childNode1, data.Dir)
   549  	require.NoError(t, err)
   550  
   551  	childPtr4 := data.BlockPointer{ID: kbfsblock.FakeID(4)}
   552  	childName4 := "child4"
   553  	_, err = ncs.GetOrCreate(
   554  		childPtr4, testPPS(childName4), parentNode, data.Dir)
   555  	require.NoError(t, err)
   556  
   557  	parentChildren := ncs.AllNodeChildren(parentNode)
   558  	require.Len(t, parentChildren, 4)
   559  
   560  	child1Children := ncs.AllNodeChildren(childNode1)
   561  	require.Len(t, child1Children, 2)
   562  
   563  	child2Children := ncs.AllNodeChildren(childNode2)
   564  	require.Len(t, child2Children, 0)
   565  
   566  	t.Log("Move child3 under the parent node.")
   567  	_, err = ncs.Move(childPtr3.Ref(), parentNode, testPPS("child3"))
   568  	require.NoError(t, err)
   569  
   570  	parentChildren = ncs.AllNodeChildren(parentNode)
   571  	require.Len(t, parentChildren, 4)
   572  
   573  	child1Children = ncs.AllNodeChildren(childNode1)
   574  	require.Len(t, child1Children, 1)
   575  }
   576  
   577  func TestNodeCacheObfuscator(t *testing.T) {
   578  	ncs := newNodeCacheStandard(
   579  		data.FolderBranch{
   580  			Tlf:    tlf.FakeID(0, tlf.Private),
   581  			Branch: data.MasterBranch,
   582  		})
   583  	ncs.SetObfuscatorMaker(func() data.Obfuscator {
   584  		return data.NewNodeObfuscator(nil)
   585  	})
   586  
   587  	t.Log("Root node should have an obfuscator")
   588  	rootPtr := data.BlockPointer{ID: kbfsblock.FakeID(0)}
   589  	rootName := "root"
   590  	rootNode, err := ncs.GetOrCreate(rootPtr, testPPS(rootName), nil, data.Dir)
   591  	require.NoError(t, err)
   592  	rootOb := rootNode.Obfuscator()
   593  	require.NotNil(t, rootOb)
   594  
   595  	t.Log("A new root node should have the same obfuscator")
   596  	rootNode2 := ncs.Get(rootPtr.Ref())
   597  	rootOb2 := rootNode2.Obfuscator()
   598  	require.NotNil(t, rootOb2)
   599  	require.True(t, rootOb == rootOb2)
   600  
   601  	t.Log("Child file should not have an obfuscator")
   602  	childPtr := data.BlockPointer{ID: kbfsblock.FakeID(1)}
   603  	childName := "child1"
   604  	childNode, err := ncs.GetOrCreate(
   605  		childPtr, testPPS(childName), rootNode, data.File)
   606  	require.NoError(t, err)
   607  	childOb := childNode.Obfuscator()
   608  	require.Nil(t, childOb)
   609  }