gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/osutil/synctree_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package osutil_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/osutil"
    30  	"github.com/snapcore/snapd/testutil"
    31  )
    32  
    33  type EnsureTreeStateSuite struct {
    34  	dir   string
    35  	globs []string
    36  }
    37  
    38  var _ = Suite(&EnsureTreeStateSuite{globs: []string{"*.snap"}})
    39  
    40  func (s *EnsureTreeStateSuite) SetUpTest(c *C) {
    41  	s.dir = c.MkDir()
    42  }
    43  
    44  func (s *EnsureTreeStateSuite) TestVerifiesExpectedFiles(c *C) {
    45  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo", "bar"), 0755), IsNil)
    46  	name := filepath.Join(s.dir, "foo", "bar", "expected.snap")
    47  	c.Assert(ioutil.WriteFile(name, []byte("expected"), 0600), IsNil)
    48  	changed, removed, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
    49  		"foo/bar": {
    50  			"expected.snap": &osutil.MemoryFileState{Content: []byte("expected"), Mode: 0600},
    51  		},
    52  	})
    53  	c.Assert(err, IsNil)
    54  	c.Check(changed, HasLen, 0)
    55  	c.Check(removed, HasLen, 0)
    56  
    57  	// The content and permissions are correct
    58  	c.Check(name, testutil.FileEquals, "expected")
    59  	stat, err := os.Stat(name)
    60  	c.Assert(err, IsNil)
    61  	c.Check(stat.Mode().Perm(), Equals, os.FileMode(0600))
    62  }
    63  
    64  func (s *EnsureTreeStateSuite) TestCreatesMissingFiles(c *C) {
    65  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo"), 0755), IsNil)
    66  
    67  	changed, removed, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
    68  		"foo": {
    69  			"missing1.snap": &osutil.MemoryFileState{Content: []byte(`content-1`), Mode: 0600},
    70  		},
    71  		"bar": {
    72  			"missing2.snap": &osutil.MemoryFileState{Content: []byte(`content-2`), Mode: 0600},
    73  		},
    74  	})
    75  	c.Assert(err, IsNil)
    76  	c.Check(changed, DeepEquals, []string{"bar/missing2.snap", "foo/missing1.snap"})
    77  	c.Check(removed, HasLen, 0)
    78  }
    79  
    80  func (s *EnsureTreeStateSuite) TestRemovesUnexpectedFiles(c *C) {
    81  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo"), 0755), IsNil)
    82  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "bar"), 0755), IsNil)
    83  	name1 := filepath.Join(s.dir, "foo", "evil1.snap")
    84  	name2 := filepath.Join(s.dir, "bar", "evil2.snap")
    85  	c.Assert(ioutil.WriteFile(name1, []byte(`evil-1`), 0600), IsNil)
    86  	c.Assert(ioutil.WriteFile(name2, []byte(`evil-2`), 0600), IsNil)
    87  
    88  	changed, removed, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
    89  		"foo": {},
    90  	})
    91  	c.Assert(err, IsNil)
    92  	c.Check(changed, HasLen, 0)
    93  	c.Check(removed, DeepEquals, []string{"bar/evil2.snap", "foo/evil1.snap"})
    94  	c.Check(name1, testutil.FileAbsent)
    95  	c.Check(name2, testutil.FileAbsent)
    96  }
    97  
    98  func (s *EnsureTreeStateSuite) TestRemovesEmptyDirectories(c *C) {
    99  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo"), 0755), IsNil)
   100  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "bar", "baz"), 0755), IsNil)
   101  	name1 := filepath.Join(s.dir, "foo", "file1.snap")
   102  	name2 := filepath.Join(s.dir, "foo", "unrelated")
   103  	name3 := filepath.Join(s.dir, "bar", "baz", "file2.snap")
   104  	c.Assert(ioutil.WriteFile(name1, []byte(`text`), 0600), IsNil)
   105  	c.Assert(ioutil.WriteFile(name2, []byte(`text`), 0600), IsNil)
   106  	c.Assert(ioutil.WriteFile(name3, []byte(`text`), 0600), IsNil)
   107  
   108  	_, _, err := osutil.EnsureTreeState(s.dir, s.globs, nil)
   109  	c.Assert(err, IsNil)
   110  
   111  	// The "foo" directory is still present, while the "bar" tree
   112  	// has been removed.
   113  	c.Check(filepath.Join(s.dir, "foo"), testutil.FilePresent)
   114  	c.Check(filepath.Join(s.dir, "bar"), testutil.FileAbsent)
   115  }
   116  
   117  func (s *EnsureTreeStateSuite) TestIgnoresUnrelatedFiles(c *C) {
   118  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo"), 0755), IsNil)
   119  	name := filepath.Join(s.dir, "foo", "unrelated")
   120  	err := ioutil.WriteFile(name, []byte(`text`), 0600)
   121  	c.Assert(err, IsNil)
   122  	changed, removed, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{})
   123  	c.Assert(err, IsNil)
   124  	// Report says that nothing has changed
   125  	c.Check(changed, HasLen, 0)
   126  	c.Check(removed, HasLen, 0)
   127  	// The file is still there
   128  	c.Check(name, testutil.FilePresent)
   129  }
   130  
   131  func (s *EnsureTreeStateSuite) TestErrorsOnBadGlob(c *C) {
   132  	_, _, err := osutil.EnsureTreeState(s.dir, []string{"["}, nil)
   133  	c.Check(err, ErrorMatches, `internal error: EnsureTreeState got invalid pattern "\[": syntax error in pattern`)
   134  }
   135  
   136  func (s *EnsureTreeStateSuite) TestErrorsOnDirectoryPathsMatchingGlobs(c *C) {
   137  	_, _, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
   138  		"foo/bar.snap/baz": nil,
   139  	})
   140  	c.Check(err, ErrorMatches, `internal error: EnsureTreeState got path "foo/bar.snap/baz" that matches glob pattern "\*.snap"`)
   141  }
   142  
   143  func (s *EnsureTreeStateSuite) TestErrorsOnFilenamesWithSlashes(c *C) {
   144  	_, _, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
   145  		"foo": {
   146  			"dir/file1.snap": &osutil.MemoryFileState{Content: []byte(`content-1`), Mode: 0600},
   147  		},
   148  	})
   149  	c.Check(err, ErrorMatches, `internal error: EnsureTreeState got filename "dir/file1.snap" in "foo", which has a path component`)
   150  }
   151  
   152  func (s *EnsureTreeStateSuite) TestErrorsOnFilenamesNotMatchingGlobs(c *C) {
   153  	_, _, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
   154  		"foo": {
   155  			"file1.not-snap": &osutil.MemoryFileState{Content: []byte(`content-1`), Mode: 0600},
   156  		},
   157  	})
   158  	c.Check(err, ErrorMatches, `internal error: EnsureTreeState got filename "file1.not-snap" in "foo", which doesn't match any glob patterns \["\*.snap"\]`)
   159  }
   160  
   161  func (s *EnsureTreeStateSuite) TestRemovesFilesOnError(c *C) {
   162  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "foo"), 0755), IsNil)
   163  	c.Assert(os.MkdirAll(filepath.Join(s.dir, "bar", "dir.snap"), 0755), IsNil)
   164  	name1 := filepath.Join(s.dir, "foo", "file1.snap")
   165  	name2 := filepath.Join(s.dir, "bar", "file2.snap")
   166  	name3 := filepath.Join(s.dir, "bar", "dir.snap", "sentinel")
   167  	c.Assert(ioutil.WriteFile(name1, []byte(`text`), 0600), IsNil)
   168  	c.Assert(ioutil.WriteFile(name2, []byte(`text`), 0600), IsNil)
   169  	c.Assert(ioutil.WriteFile(name3, []byte(`text`), 0600), IsNil)
   170  
   171  	changed, removed, err := osutil.EnsureTreeState(s.dir, s.globs, map[string]map[string]osutil.FileState{
   172  		"foo": {
   173  			"file1.snap": &osutil.MemoryFileState{Content: []byte(`content-1`), Mode: 0600},
   174  		},
   175  	})
   176  	c.Check(err, ErrorMatches, `remove .*/bar/dir.snap: directory not empty`)
   177  	c.Check(changed, HasLen, 0)
   178  	c.Check(removed, DeepEquals, []string{"bar/file2.snap", "foo/file1.snap"})
   179  
   180  	// Matching files have been removed, along with the empty directory
   181  	c.Check(name1, testutil.FileAbsent)
   182  	c.Check(filepath.Dir(name1), testutil.FileAbsent)
   183  	c.Check(name2, testutil.FileAbsent)
   184  
   185  	// But the unmatched file in the bad directory remains
   186  	c.Check(name3, testutil.FilePresent)
   187  }