github.com/getgauge/gauge@v1.6.9/api/infoGatherer/specDetails_test.go (about)

     1  /*----------------------------------------------------------------
     2   *  Copyright (c) ThoughtWorks, Inc.
     3   *  Licensed under the Apache License, Version 2.0
     4   *  See LICENSE in the project root for license information.
     5   *----------------------------------------------------------------*/
     6  
     7  package infoGatherer
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/getgauge/gauge/config"
    16  	"github.com/getgauge/gauge/gauge"
    17  	"github.com/getgauge/gauge/util"
    18  	. "gopkg.in/check.v1"
    19  )
    20  
    21  func Test(t *testing.T) { TestingT(t) }
    22  
    23  const specDir = "specs"
    24  
    25  var _ = Suite(&MySuite{})
    26  
    27  var concept1 []byte
    28  var concept2 []byte
    29  var concept3 []byte
    30  var concept4 []byte
    31  var spec1 []byte
    32  var spec2 []byte
    33  var spec3 []byte
    34  var specWithTags []byte
    35  var spec2WithTags []byte
    36  var specWithConcept []byte
    37  
    38  type MySuite struct {
    39  	specsDir   string
    40  	projectDir string
    41  }
    42  
    43  func (s *MySuite) SetUpTest(c *C) {
    44  	s.projectDir, _ = os.MkdirTemp("_testdata", "gaugeTest")
    45  	s.specsDir, _ = createDirIn(s.projectDir, specDir)
    46  	config.ProjectRoot = s.projectDir
    47  
    48  	s.buildTestData()
    49  }
    50  
    51  func (s *MySuite) TearDownTest(c *C) {
    52  	os.RemoveAll(s.projectDir)
    53  }
    54  
    55  func (s *MySuite) buildTestData() {
    56  	concept1 = make([]byte, 0)
    57  	concept1 = append(concept1, `# foo bar
    58  * first step with "foo"
    59  * say "hello" to me
    60  * a "final" step
    61  `...)
    62  
    63  	concept2 = make([]byte, 0)
    64  	concept2 = append(concept2, `# bar
    65  * first step with "foo"
    66  * say "hello" to me
    67  * a "final" step
    68  `...)
    69  
    70  	concept3 = make([]byte, 0)
    71  	concept3 = append(concept3, `# foo bar with <param> having errors
    72  * first step with "foo"
    73  * say <param> to me
    74  * a <final> step
    75  `...)
    76  
    77  	concept4 = make([]byte, 0)
    78  	concept4 = append(concept4, `# foo bar with 1 step
    79  * say hello
    80  `...)
    81  
    82  	spec1 = make([]byte, 0)
    83  	spec1 = append(spec1, `Specification Heading
    84  =====================
    85  Scenario 1
    86  ----------
    87  * say hello
    88  * say "hello" to me
    89  `...)
    90  
    91  	spec2 = make([]byte, 0)
    92  	spec2 = append(spec2, `Specification Heading
    93  =====================
    94  Scenario 1
    95  ----------
    96  * say hello
    97  * say "hello" to me
    98  * say "bye" to me
    99  `...)
   100  
   101  	spec3 = make([]byte, 0)
   102  	spec3 = append(spec3, `Specification Heading
   103  =====================
   104  |Col1|Col2|
   105  |----|----|
   106  |Val1|Val2|
   107  
   108  Scenario with parse errors
   109  ----------
   110  * say hello
   111  * say "hello" to me
   112  * say <bye> to me
   113  `...)
   114  
   115  	specWithTags = make([]byte, 0)
   116  	specWithTags = append(specWithTags, `Specification Heading
   117  =====================
   118  tags:foo, bar, hello
   119  
   120  Scenario with tags
   121  ----------
   122  tags: simple, complex
   123  
   124  * say hello
   125  * say "hello" to me
   126  * say <bye> to me
   127  `...)
   128  
   129  	spec2WithTags = make([]byte, 0)
   130  	spec2WithTags = append(spec2WithTags, `Specification Heading
   131  =====================
   132  tags:foo, another
   133  
   134  Scenario with tags
   135  ----------
   136  tags: simple, complex
   137  
   138  * say hello
   139  * say "hello" to me
   140  * say <bye> to me
   141  `...)
   142  
   143  	specWithConcept = make([]byte, 0)
   144  	specWithConcept = append(specWithConcept, `Specification Heading
   145  =====================
   146  tags:foo, another
   147  
   148  Scenario with tags
   149  ----------
   150  tags: simple, complex
   151  
   152  * say hello
   153  * foo bar with 1 step
   154  `...)
   155  
   156  }
   157  
   158  func (s *MySuite) TestGetParsedSpecs(c *C) {
   159  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   160  	c.Assert(err, Equals, nil)
   161  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{specDir}}
   162  
   163  	specFiles := util.FindSpecFilesIn(s.specsDir)
   164  	details := specInfoGatherer.getParsedSpecs(specFiles)
   165  
   166  	c.Assert(len(details), Equals, 1)
   167  	c.Assert(details[0].Spec.Heading.Value, Equals, "Specification Heading")
   168  }
   169  
   170  func (s *MySuite) TestGetParsedSpecsForInvalidFile(c *C) {
   171  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{specDir}}
   172  
   173  	details := specInfoGatherer.getParsedSpecs([]string{"spec1.spec"})
   174  
   175  	c.Assert(len(details), Equals, 1)
   176  	c.Assert(len(details[0].Errs), Equals, 1)
   177  	c.Assert(details[0].Errs[0].Message, Equals, "File spec1.spec doesn't exist.")
   178  }
   179  
   180  func (s *MySuite) TestGetParsedConcepts(c *C) {
   181  	_, err := createFileIn(s.specsDir, "concept.cpt", concept1)
   182  	c.Assert(err, Equals, nil)
   183  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.projectDir + string(filepath.Separator) + specDir}}
   184  
   185  	conceptsMap := specInfoGatherer.getParsedConcepts()
   186  
   187  	c.Assert(len(conceptsMap), Equals, 1)
   188  	c.Assert(conceptsMap["foo bar"], NotNil)
   189  	c.Assert(specInfoGatherer.conceptDictionary, NotNil)
   190  }
   191  
   192  func (s *MySuite) TestInitSpecsCache(c *C) {
   193  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   194  	c.Assert(err, Equals, nil)
   195  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   196  	specInfoGatherer.waitGroup.Add(1)
   197  
   198  	specInfoGatherer.initSpecsCache()
   199  
   200  	c.Assert(len(specInfoGatherer.specsCache.specDetails), Equals, 1)
   201  }
   202  
   203  func (s *MySuite) TestInitConceptsCache(c *C) {
   204  	_, err := createFileIn(s.specsDir, "concept1.cpt", concept1)
   205  	c.Assert(err, Equals, nil)
   206  	_, err = createFileIn(s.specsDir, "concept2.cpt", concept2)
   207  	c.Assert(err, Equals, nil)
   208  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.projectDir + string(filepath.Separator) + specDir}}
   209  	specInfoGatherer.waitGroup.Add(1)
   210  
   211  	specInfoGatherer.initConceptsCache()
   212  
   213  	c.Assert(len(specInfoGatherer.conceptsCache.concepts), Equals, 2)
   214  }
   215  
   216  func (s *MySuite) TestInitStepsCache(c *C) {
   217  	f, _ := createFileIn(s.specsDir, "spec1.spec", spec1)
   218  	f, _ = filepath.Abs(f)
   219  	f1, _ := createFileIn(s.specsDir, "concept2.cpt", concept2)
   220  	f1, _ = filepath.Abs(f1)
   221  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   222  	specInfoGatherer.waitGroup.Add(3)
   223  
   224  	specInfoGatherer.initConceptsCache()
   225  	specInfoGatherer.initSpecsCache()
   226  	specInfoGatherer.initStepsCache()
   227  	c.Assert(len(specInfoGatherer.stepsCache.steps[f]), Equals, 2)
   228  	c.Assert(len(specInfoGatherer.stepsCache.steps[f1]), Equals, 3)
   229  
   230  }
   231  
   232  func (s *MySuite) TestInitTagsCache(c *C) {
   233  	_, err := createFileIn(s.specsDir, "specWithTags.spec", specWithTags)
   234  	if err != nil {
   235  		c.Error(err)
   236  	}
   237  
   238  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   239  	specInfoGatherer.waitGroup.Add(2)
   240  
   241  	specInfoGatherer.initSpecsCache()
   242  	specInfoGatherer.initTagsCache()
   243  	c.Assert(len(specInfoGatherer.Tags()), Equals, 5)
   244  }
   245  
   246  func (s *MySuite) TestInitTagsCacheWithMultipleFiles(c *C) {
   247  	_, err := createFileIn(s.specsDir, "specWithTags.spec", specWithTags)
   248  	if err != nil {
   249  		c.Error(err)
   250  	}
   251  
   252  	_, err = createFileIn(s.specsDir, "spec2WithTags.spec", spec2WithTags)
   253  	if err != nil {
   254  		c.Error(err)
   255  	}
   256  
   257  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   258  	specInfoGatherer.waitGroup.Add(2)
   259  
   260  	specInfoGatherer.initSpecsCache()
   261  	specInfoGatherer.initTagsCache()
   262  	c.Assert(len(specInfoGatherer.Tags()), Equals, 6)
   263  }
   264  
   265  func (s *MySuite) TestGetStepsFromCachedSpecs(c *C) {
   266  	f, _ := createFileIn(s.specsDir, "spec1.spec", spec1)
   267  	f, _ = filepath.Abs(f)
   268  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   269  	specInfoGatherer.waitGroup.Add(3)
   270  	specInfoGatherer.initSpecsCache()
   271  
   272  	stepsFromSpecsMap := specInfoGatherer.getStepsFromCachedSpecs()
   273  	c.Assert(len(stepsFromSpecsMap[f]), Equals, 2)
   274  	c.Assert(stepsFromSpecsMap[f][0].Value, Equals, "say hello")
   275  	c.Assert(stepsFromSpecsMap[f][1].Value, Equals, "say {} to me")
   276  }
   277  
   278  func (s *MySuite) TestGetStepsFromCachedConcepts(c *C) {
   279  	f, _ := createFileIn(s.specsDir, "concept1.cpt", concept1)
   280  	f, _ = filepath.Abs(f)
   281  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   282  	specInfoGatherer.waitGroup.Add(3)
   283  	specInfoGatherer.initSpecsCache()
   284  	specInfoGatherer.initConceptsCache()
   285  
   286  	stepsFromConceptsMap := specInfoGatherer.getStepsFromCachedConcepts()
   287  	c.Assert(len(stepsFromConceptsMap[f]), Equals, 3)
   288  	c.Assert(stepsFromConceptsMap[f][0].Value, Equals, "first step with {}")
   289  	c.Assert(stepsFromConceptsMap[f][1].Value, Equals, "say {} to me")
   290  	c.Assert(stepsFromConceptsMap[f][2].Value, Equals, "a {} step")
   291  }
   292  
   293  func (s *MySuite) TestGetAvailableSteps(c *C) {
   294  	var steps []*gauge.Step
   295  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   296  	if err != nil {
   297  		c.Error(err)
   298  	}
   299  
   300  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   301  	specInfoGatherer.waitGroup.Add(2)
   302  	specInfoGatherer.initSpecsCache()
   303  	specInfoGatherer.initStepsCache()
   304  
   305  	steps = specInfoGatherer.Steps(true)
   306  	c.Assert(len(steps), Equals, 2)
   307  	if !hasStep(steps, "say hello") {
   308  		c.Fatalf("Step value not found %s", "say hello")
   309  	}
   310  	if !hasStep(steps, "say {} to me") {
   311  		c.Fatalf("Step value not found %s", "say {} to me")
   312  	}
   313  }
   314  
   315  func (s *MySuite) TestGetAvailableStepsShouldFilterDuplicates(c *C) {
   316  	var steps []*gauge.Step
   317  	_, err := createFileIn(s.specsDir, "spec2.spec", spec2)
   318  	if err != nil {
   319  		c.Error(err)
   320  	}
   321  
   322  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   323  	specInfoGatherer.waitGroup.Add(2)
   324  	specInfoGatherer.initSpecsCache()
   325  	specInfoGatherer.initStepsCache()
   326  
   327  	steps = specInfoGatherer.Steps(true)
   328  	c.Assert(len(steps), Equals, 2)
   329  	if !hasStep(steps, "say hello") {
   330  		c.Fatalf("Step value not found %s", "say hello")
   331  	}
   332  	if !hasStep(steps, "say {} to me") {
   333  		c.Fatalf("Step value not found %s", "say {} to me")
   334  	}
   335  }
   336  
   337  func (s *MySuite) TestGetAvailableStepsShouldFilterConcepts(c *C) {
   338  	var steps []*gauge.Step
   339  	_, err := createFileIn(s.specsDir, "concept1.cpt", concept4)
   340  	if err != nil {
   341  		c.Error(err)
   342  	}
   343  
   344  	_, err = createFileIn(s.specsDir, "spec1.spec", specWithConcept)
   345  	if err != nil {
   346  		c.Error(err)
   347  	}
   348  
   349  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   350  	specInfoGatherer.waitGroup.Add(3)
   351  	specInfoGatherer.initConceptsCache()
   352  	specInfoGatherer.initSpecsCache()
   353  	specInfoGatherer.initStepsCache()
   354  
   355  	steps = specInfoGatherer.Steps(true)
   356  	c.Assert(len(steps), Equals, 1)
   357  	if hasStep(steps, "foo bar with 1 step") {
   358  		c.Fatalf("Step value found %s", "foo bar with 1 step")
   359  	}
   360  	steps = specInfoGatherer.Steps(false)
   361  	c.Assert(len(steps), Equals, 2)
   362  	if !hasStep(steps, "foo bar with 1 step") {
   363  		c.Fatalf("Step value not found %s", "foo bar with 1 step")
   364  	}
   365  }
   366  
   367  func (s *MySuite) TestGetAvailableAllStepsShouldFilterConcepts(c *C) {
   368  	var steps []*gauge.Step
   369  	_, err := createFileIn(s.specsDir, "concept1.cpt", concept4)
   370  	if err != nil {
   371  		c.Error(err)
   372  	}
   373  
   374  	_, err = createFileIn(s.specsDir, "spec1.spec", specWithConcept)
   375  	if err != nil {
   376  		c.Error(err)
   377  	}
   378  
   379  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   380  	specInfoGatherer.waitGroup.Add(3)
   381  	specInfoGatherer.initConceptsCache()
   382  	specInfoGatherer.initSpecsCache()
   383  	specInfoGatherer.initStepsCache()
   384  
   385  	steps = specInfoGatherer.AllSteps(true)
   386  	c.Assert(len(steps), Equals, 2)
   387  	if hasStep(steps, "foo bar with 1 step") {
   388  		c.Fatalf("Step value found %s", "foo bar with 1 step")
   389  	}
   390  	steps = specInfoGatherer.AllSteps(false)
   391  	c.Assert(len(steps), Equals, 3)
   392  	if !hasStep(steps, "foo bar with 1 step") {
   393  		c.Fatalf("Step value not found %s", "foo bar with 1 step")
   394  	}
   395  }
   396  
   397  func hasStep(steps []*gauge.Step, stepText string) bool {
   398  	for _, step := range steps {
   399  		if step.Value == stepText {
   400  			return true
   401  		}
   402  	}
   403  	return false
   404  }
   405  
   406  func (s *MySuite) TestHasSpecForSpecDetail(c *C) {
   407  	c.Assert((&SpecDetail{}).HasSpec(), Equals, false)
   408  	c.Assert((&SpecDetail{Spec: &gauge.Specification{}}).HasSpec(), Equals, false)
   409  	c.Assert((&SpecDetail{Spec: &gauge.Specification{Heading: &gauge.Heading{}}}).HasSpec(), Equals, true)
   410  }
   411  
   412  func (s *MySuite) TestGetAvailableSpecDetails(c *C) {
   413  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   414  	c.Assert(err, Equals, nil)
   415  	sig := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}, specsCache: specsCache{specDetails: make(map[string]*SpecDetail)}}
   416  	specFiles := util.FindSpecFilesIn(s.specsDir)
   417  	sig.specsCache.specDetails[specFiles[0]] = &SpecDetail{Spec: &gauge.Specification{Heading: &gauge.Heading{Value: "Specification Heading"}}}
   418  
   419  	details := sig.GetAvailableSpecDetails(specFiles)
   420  
   421  	c.Assert(len(details), Equals, 1)
   422  	c.Assert(details[0].Spec.Heading.Value, Equals, "Specification Heading")
   423  }
   424  
   425  func (s *MySuite) TestGetAvailableSpecDetailsInDefaultDir(c *C) {
   426  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   427  	c.Assert(err, Equals, nil)
   428  	wd, _ := os.Getwd()
   429  	err = os.Chdir(s.projectDir)
   430  	if err != nil {
   431  		c.Error(err)
   432  	}
   433  	defer func() {
   434  		err := os.Chdir(wd)
   435  		if err != nil {
   436  			c.Error(err)
   437  		}
   438  	}()
   439  	sig := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}, specsCache: specsCache{specDetails: make(map[string]*SpecDetail)}}
   440  	specFiles := util.FindSpecFilesIn(specDir)
   441  	sig.specsCache.specDetails[specFiles[0]] = &SpecDetail{Spec: &gauge.Specification{Heading: &gauge.Heading{Value: "Specification Heading"}}}
   442  
   443  	details := sig.GetAvailableSpecDetails([]string{})
   444  
   445  	c.Assert(len(details), Equals, 1)
   446  	c.Assert(details[0].Spec.Heading.Value, Equals, "Specification Heading")
   447  }
   448  
   449  func (s *MySuite) TestGetAvailableSpecDetailsWithEmptyCache(c *C) {
   450  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   451  	c.Assert(err, Equals, nil)
   452  	sig := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   453  
   454  	details := sig.GetAvailableSpecDetails([]string{})
   455  
   456  	c.Assert(len(details), Equals, 0)
   457  }
   458  
   459  func (s *MySuite) TestParamsForStepFile(c *C) {
   460  	file, _ := createFileIn(s.specsDir, "spec3.spec", spec3)
   461  	file, _ = filepath.Abs(file)
   462  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   463  	specInfoGatherer.waitGroup.Add(2)
   464  	specInfoGatherer.initConceptsCache()
   465  	specInfoGatherer.initSpecsCache()
   466  	specInfoGatherer.initStepsCache()
   467  	specInfoGatherer.initParamsCache()
   468  
   469  	staticParams := specInfoGatherer.Params(file, gauge.Static)
   470  	c.Assert(len(staticParams), Equals, 1)
   471  	dynamicParams := specInfoGatherer.Params(file, gauge.Dynamic)
   472  	c.Assert(len(dynamicParams), Equals, 3)
   473  	hasParam := func(param string, list []gauge.StepArg) bool {
   474  		for _, p := range list {
   475  			if p.ArgValue() == param {
   476  				return true
   477  			}
   478  		}
   479  		return false
   480  	}
   481  	if !hasParam("hello", staticParams) {
   482  		c.Errorf(`Param "hello" not found`)
   483  	}
   484  	if !hasParam("bye", dynamicParams) {
   485  		c.Errorf(`Param "bye" not found`)
   486  	}
   487  	if !hasParam("Col1", dynamicParams) {
   488  		c.Errorf(`Param "Col1" not found`)
   489  	}
   490  	if !hasParam("Col2", dynamicParams) {
   491  		c.Errorf(`Param "Col1" not found`)
   492  	}
   493  }
   494  
   495  func (s *MySuite) TestParamsForConceptFile(c *C) {
   496  	file, _ := createFileIn(s.specsDir, "concept3.cpt", concept3)
   497  	file, _ = filepath.Abs(file)
   498  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   499  	specInfoGatherer.waitGroup.Add(2)
   500  	specInfoGatherer.initConceptsCache()
   501  	specInfoGatherer.initSpecsCache()
   502  	specInfoGatherer.initStepsCache()
   503  	specInfoGatherer.initParamsCache()
   504  
   505  	staticParams := specInfoGatherer.Params(file, gauge.Static)
   506  	c.Assert(len(staticParams), Equals, 1)
   507  	dynamicParams := specInfoGatherer.Params(file, gauge.Dynamic)
   508  	c.Assert(len(dynamicParams), Equals, 2)
   509  	hasParam := func(param string, list []gauge.StepArg) bool {
   510  		for _, p := range list {
   511  			if p.ArgValue() == param {
   512  				return true
   513  			}
   514  		}
   515  		return false
   516  	}
   517  	if !hasParam("foo", staticParams) {
   518  		c.Errorf(`Param "foo" not found`)
   519  	}
   520  	if !hasParam("param", dynamicParams) {
   521  		c.Errorf(`Param "param" not found`)
   522  	}
   523  	if !hasParam("final", dynamicParams) {
   524  		c.Errorf(`Param "final" not found`)
   525  	}
   526  }
   527  
   528  func (s *MySuite) TestAllStepsOnFileRename(c *C) {
   529  	_, err := createFileIn(s.specsDir, "spec1.spec", spec1)
   530  	if err != nil {
   531  		c.Error(err)
   532  	}
   533  
   534  	specInfoGatherer := &SpecInfoGatherer{SpecDirs: []string{s.specsDir}}
   535  	specInfoGatherer.initSpecsCache()
   536  	specInfoGatherer.initStepsCache()
   537  
   538  	c.Assert(len(specInfoGatherer.AllSteps(true)), Equals, 2)
   539  	_, err = renameFileIn(s.specsDir, "spec1.spec", "spec42.spec")
   540  	if err != nil {
   541  		c.Error(err)
   542  	}
   543  
   544  	c.Assert(len(specInfoGatherer.AllSteps(true)), Equals, 2)
   545  }
   546  
   547  func createFileIn(dir string, fileName string, data []byte) (string, error) {
   548  	err := os.MkdirAll(dir, 0750)
   549  	if err != nil {
   550  		return "", fmt.Errorf("unable to create %s: %s", dir, err.Error())
   551  	}
   552  
   553  	err = os.WriteFile(filepath.Join(dir, fileName), data, 0644)
   554  	return filepath.Join(dir, fileName), err
   555  }
   556  
   557  func renameFileIn(dir string, oldFileName string, newFileName string) (string, error) {
   558  	err := os.Rename(filepath.Join(dir, oldFileName), filepath.Join(dir, newFileName))
   559  	return filepath.Join(dir, newFileName), err
   560  }
   561  
   562  func createDirIn(dir string, dirName string) (string, error) {
   563  	tempDir, _ := os.MkdirTemp(dir, dirName)
   564  	fullDirName := filepath.Join(dir, dirName)
   565  	err := os.Rename(tempDir, fullDirName)
   566  	return fullDirName, err
   567  }