github.com/openconfig/goyang@v1.4.5/pkg/yang/identity_test.go (about)

     1  // Copyright 2016 Google Inc.
     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  
    15  package yang
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	"github.com/openconfig/gnmi/errdiff"
    22  )
    23  
    24  // inputModule is a mock input YANG module.
    25  type inputModule struct {
    26  	name    string // The filename of the YANG module.
    27  	content string // The contents of the YANG module.
    28  }
    29  
    30  type idrefOut struct {
    31  	module string   // The module that the identityref is within.
    32  	name   string   // The name of the identityref.
    33  	values []string // Names of the identities that the identityref relates to.
    34  }
    35  
    36  // identityOut is the output for a particular identity within the test case.
    37  type identityOut struct {
    38  	module    string   // The module that the identity is within.
    39  	name      string   // The name of the identity.
    40  	baseNames []string // The base(s) of the identity as string(s).
    41  	values    []string // The string names of derived identities.
    42  }
    43  
    44  // identityTestCase is a test case for a module which contains identities.
    45  type identityTestCase struct {
    46  	name          string
    47  	in            []inputModule // The set of input modules for the test
    48  	identities    []identityOut // Slice of the identity values expected
    49  	idrefs        []idrefOut    // Slice of identityref results expected
    50  	wantErrSubstr string        // wanErrSubstr is a substring of the wanted error.
    51  }
    52  
    53  // getBaseNamesFrom is a utility function for getting the base name(s) of an identity
    54  func getBaseNamesFrom(i *Identity) []string {
    55  	baseNames := []string{}
    56  	for _, base := range i.Base {
    57  		baseNames = append(baseNames, base.Name)
    58  	}
    59  	return baseNames
    60  }
    61  
    62  // Test cases for basic identity extraction.
    63  var basicTestCases = []identityTestCase{
    64  	{
    65  		name: "basic-test-case-1: Check identity is found in module.",
    66  		in: []inputModule{
    67  			{
    68  				name: "idtest-one",
    69  				content: `
    70  					module idtest-one {
    71  					  namespace "urn:idone";
    72  					  prefix "idone";
    73  
    74  					  identity TEST_ID;
    75  					}
    76  				`},
    77  		},
    78  		identities: []identityOut{
    79  			{module: "idtest-one", name: "TEST_ID"},
    80  		},
    81  	},
    82  	{
    83  		name: "basic-test-case-2: Check identity with base is found in module.",
    84  		in: []inputModule{
    85  			{
    86  				name: "idtest-two",
    87  				content: `
    88  					module idtest-two {
    89  					  namespace "urn:idtwo";
    90  					  prefix "idone";
    91  
    92  					  identity TEST_ID;
    93  					  identity TEST_ID_TWO;
    94  					  identity TEST_CHILD {
    95  					    base TEST_ID;
    96  					  }
    97  					}
    98  				`},
    99  		},
   100  		identities: []identityOut{
   101  			{module: "idtest-two", name: "TEST_ID"},
   102  			{module: "idtest-two", name: "TEST_ID_TWO"},
   103  			{module: "idtest-two", name: "TEST_CHILD", baseNames: []string{"TEST_ID"}},
   104  		},
   105  	},
   106  	{
   107  		name: "basic-test-case-3: Check identity with multiple bases.",
   108  		in: []inputModule{
   109  			{
   110  				name: "idtest-three",
   111  				content: `
   112  					module idtest-three {
   113  					  namespace "urn:idthree";
   114  					  prefix "idthree";
   115  
   116  					  identity BASE_ONE;
   117  					  identity BASE_TWO;
   118  					  identity TEST_CHILD_WITH_MULTIPLE_BASES {
   119  						base BASE_ONE;
   120  						base BASE_TWO;
   121  					  }
   122  					}
   123  				`},
   124  		},
   125  		identities: []identityOut{
   126  			{module: "idtest-three", name: "BASE_ONE"},
   127  			{module: "idtest-three", name: "BASE_TWO"},
   128  			{module: "idtest-three", name: "TEST_CHILD_WITH_MULTIPLE_BASES", baseNames: []string{"BASE_ONE", "BASE_TWO"}},
   129  		},
   130  	},
   131  	{
   132  		name: "basic-test-case-4: Check identity base is found from submodule.",
   133  		in: []inputModule{
   134  			{
   135  				name: "idtest-one",
   136  				content: `
   137  					module idtest-one {
   138  					  namespace "urn:idone";
   139  					  prefix "idone";
   140  
   141  					  include "idtest-one-sub";
   142  
   143  					  identity TEST_ID_DERIVED {
   144  					    base TEST_ID;
   145  					  }
   146  					}
   147  				`},
   148  			{
   149  				name: "idtest-one-sub",
   150  				content: `
   151  					submodule idtest-one-sub {
   152  					  belongs-to idtest-one {
   153  					    prefix "idone";
   154  					  }
   155  
   156  					  identity TEST_ID;
   157  					}
   158  				`},
   159  		},
   160  		identities: []identityOut{
   161  			{module: "idtest-one", name: "TEST_ID"},
   162  			{module: "idtest-one", name: "TEST_ID_DERIVED", baseNames: []string{"TEST_ID"}},
   163  		},
   164  	},
   165  	{
   166  		name: "basic-test-case-5: Check identity base is found from module.",
   167  		in: []inputModule{
   168  			{
   169  				name: "idtest-one",
   170  				content: `
   171  					module idtest-one {
   172  					  namespace "urn:idone";
   173  					  prefix "idone";
   174  
   175  					  include "idtest-one-sub";
   176  
   177  					  identity TEST_ID;
   178  					}
   179  				`},
   180  			{
   181  				name: "idtest-one-sub",
   182  				content: `
   183  					submodule idtest-one-sub {
   184  					  belongs-to idtest-one {
   185  					    prefix "idone";
   186  					  }
   187  
   188  					  identity TEST_ID_DERIVED {
   189  					    base TEST_ID;
   190  					  }
   191  					}
   192  				`},
   193  		},
   194  		identities: []identityOut{
   195  			{module: "idtest-one", name: "TEST_ID_DERIVED", baseNames: []string{"TEST_ID"}},
   196  			{module: "idtest-one", name: "TEST_ID"},
   197  		},
   198  	},
   199  }
   200  
   201  // Test the ability to extract identities from a module with the correct base
   202  // statements.
   203  func TestIdentityExtract(t *testing.T) {
   204  	for _, tt := range basicTestCases {
   205  		ms := NewModules()
   206  		for _, mod := range tt.in {
   207  			_ = ms.Parse(mod.content, mod.name)
   208  		}
   209  
   210  		for _, ti := range tt.identities {
   211  			_, err := ms.GetModule(ti.module)
   212  
   213  			if err != nil {
   214  				t.Errorf("Could not parse module : %s", ti.module)
   215  				continue
   216  			}
   217  
   218  			foundIdentity := false
   219  			var thisID *Identity
   220  			for _, ri := range ms.typeDict.identities.dict {
   221  				moduleName := module(ri.Module).Name
   222  				if ri.Identity.Name == ti.name && moduleName == ti.module {
   223  					foundIdentity = true
   224  					thisID = ri.Identity
   225  					break
   226  				}
   227  			}
   228  
   229  			if !foundIdentity {
   230  				t.Errorf("Could not find identity %s in module %s, identity dict:\n%+v", ti.name, ti.module, ms.typeDict.identities.dict)
   231  				continue
   232  			}
   233  
   234  			actualBaseNames := getBaseNamesFrom(thisID)
   235  			if len(ti.baseNames) > 0 {
   236  				if diff := cmp.Diff(actualBaseNames, ti.baseNames); diff != "" {
   237  					t.Errorf("(-got, +want):\n%s", diff)
   238  				}
   239  			} else {
   240  				if thisID.Base != nil {
   241  					t.Errorf("Identity %s had unexpected base(s) %s", thisID.Name,
   242  						actualBaseNames)
   243  				}
   244  			}
   245  		}
   246  	}
   247  }
   248  
   249  // Test cases for validating that identities can be resolved correctly.
   250  var treeTestCases = []identityTestCase{
   251  	{
   252  		name: "tree-test-case-0: Validate identity resolution across submodules",
   253  		in: []inputModule{
   254  			{
   255  				name: "base.yang",
   256  				content: `
   257  				  module base {
   258  				    namespace "urn:base";
   259  				    prefix "base";
   260  
   261  				    include side;
   262  
   263  				    identity REMOTE_BASE;
   264  				  }
   265  				`},
   266  			{
   267  				name: "remote.yang",
   268  				content: `
   269  				  submodule side {
   270  				    belongs-to base {
   271  				      prefix "r";
   272  				    }
   273  
   274  				    identity LOCAL_REMOTE_BASE {
   275  				      base r:REMOTE_BASE;
   276  				    }
   277  				  }
   278  				`},
   279  		},
   280  		identities: []identityOut{
   281  			{
   282  				module: "base",
   283  				name:   "REMOTE_BASE",
   284  				values: []string{"LOCAL_REMOTE_BASE"},
   285  			},
   286  		},
   287  	},
   288  	{
   289  		name: "tree-test-case-1: Validate identity resolution across modules",
   290  		in: []inputModule{
   291  			{
   292  				name: "base.yang",
   293  				content: `
   294  				  module base {
   295  				    namespace "urn:base";
   296  				    prefix "base";
   297  
   298  				    import remote { prefix "r"; }
   299  				    import remote2 { prefix "r2"; }
   300  
   301  				    identity LOCAL_REMOTE_BASE {
   302  				      base r:REMOTE_BASE;
   303  				    }
   304  
   305  				    identity LOCAL_REMOTE_BASE2 {
   306  				      base r2:REMOTE_BASE2;
   307  				    }
   308  				  }
   309  				`},
   310  			{
   311  				name: "remote.yang",
   312  				content: `
   313  				  module remote {
   314  				    namespace "urn:remote";
   315  				    prefix "r";
   316  
   317  				    identity REMOTE_BASE;
   318  				  }
   319  				`},
   320  			{
   321  				name: "remote2.yang",
   322  				content: `
   323  				  module remote2 {
   324  				    namespace "urn:remote2";
   325  				    prefix "remote";
   326  
   327  				    identity REMOTE_BASE2;
   328  				  }
   329  				`},
   330  		},
   331  		identities: []identityOut{
   332  			{
   333  				module: "remote",
   334  				name:   "REMOTE_BASE",
   335  				values: []string{"LOCAL_REMOTE_BASE"},
   336  			},
   337  			{
   338  				module: "remote2",
   339  				name:   "REMOTE_BASE2",
   340  				values: []string{"LOCAL_REMOTE_BASE2"},
   341  			},
   342  			{
   343  				module:    "base",
   344  				name:      "LOCAL_REMOTE_BASE",
   345  				baseNames: []string{"r:REMOTE_BASE"},
   346  			},
   347  			{
   348  				module:    "base",
   349  				name:      "LOCAL_REMOTE_BASE2",
   350  				baseNames: []string{"r2:REMOTE_BASE2"},
   351  			},
   352  		},
   353  	},
   354  	{
   355  		name: "tree-test-case-2: Multi-level inheritance validation.",
   356  		in: []inputModule{
   357  			{
   358  				name: "base.yang",
   359  				content: `
   360  				  module base {
   361  				    namespace "urn:base";
   362  				    prefix "base";
   363  
   364  				    identity GREATGRANDFATHER;
   365  						identity GRANDFATHER {
   366  							base "GREATGRANDFATHER";
   367  						}
   368  						identity FATHER {
   369  							base "GRANDFATHER";
   370  						}
   371  						identity SON {
   372  							base "FATHER";
   373  						}
   374  						identity UNCLE {
   375  							base "GRANDFATHER";
   376  						}
   377  						identity BROTHER {
   378  							base "FATHER";
   379  						}
   380  						identity GREATUNCLE {
   381  							base "GREATGRANDFATHER";
   382  						}
   383  				  }
   384  				`},
   385  		},
   386  		identities: []identityOut{
   387  			{
   388  				module: "base",
   389  				name:   "GREATGRANDFATHER",
   390  				values: []string{
   391  					"BROTHER", // Order is alphabetical
   392  					"FATHER",
   393  					"GRANDFATHER",
   394  					"GREATUNCLE",
   395  					"SON",
   396  					"UNCLE",
   397  				},
   398  			},
   399  			{
   400  				module:    "base",
   401  				name:      "GRANDFATHER",
   402  				baseNames: []string{"GREATGRANDFATHER"},
   403  				values:    []string{"BROTHER", "FATHER", "SON", "UNCLE"},
   404  			},
   405  			{
   406  				module:    "base",
   407  				name:      "GREATUNCLE",
   408  				baseNames: []string{"GREATGRANDFATHER"},
   409  			},
   410  			{
   411  				module:    "base",
   412  				name:      "FATHER",
   413  				baseNames: []string{"GRANDFATHER"},
   414  				values:    []string{"BROTHER", "SON"},
   415  			},
   416  			{
   417  				module:    "base",
   418  				name:      "UNCLE",
   419  				baseNames: []string{"GRANDFATHER"},
   420  			},
   421  			{
   422  				module:    "base",
   423  				name:      "BROTHER",
   424  				baseNames: []string{"FATHER"},
   425  			},
   426  		},
   427  	},
   428  	{
   429  		name: "tree-test-case-3",
   430  		in: []inputModule{
   431  			{
   432  				name: "base.yang",
   433  				content: `
   434  				  module base {
   435  				    namespace "urn:base";
   436  				    prefix "base";
   437  
   438  						identity BASE;
   439  						identity NOTBASE {
   440  							base BASE;
   441  						}
   442  
   443  						leaf idref {
   444  							type identityref {
   445  								base "BASE";
   446  							}
   447  						}
   448  				  }
   449  				`},
   450  		},
   451  		identities: []identityOut{
   452  			{
   453  				module: "base",
   454  				name:   "BASE",
   455  				values: []string{"NOTBASE"},
   456  			},
   457  			{
   458  				module:    "base",
   459  				name:      "NOTBASE",
   460  				baseNames: []string{"BASE"},
   461  			},
   462  		},
   463  		idrefs: []idrefOut{
   464  			{
   465  				module: "base",
   466  				name:   "idref",
   467  				values: []string{"NOTBASE"},
   468  			},
   469  		},
   470  	},
   471  	{
   472  		name: "tree-test-case-4",
   473  		in: []inputModule{
   474  			{
   475  				name: "base.yang",
   476  				content: `
   477  				  module base4 {
   478  				    namespace "urn:base";
   479  				    prefix "base4";
   480  
   481  						identity BASE4;
   482  						identity CHILD4 {
   483  							base BASE4;
   484  						}
   485  
   486  						typedef t {
   487  							type identityref {
   488  								base BASE4;
   489  							}
   490  						}
   491  
   492  						leaf tref {
   493  							type t;
   494  						}
   495  				  }
   496  				`},
   497  		},
   498  		identities: []identityOut{
   499  			{
   500  				module: "base4",
   501  				name:   "BASE4",
   502  				values: []string{"CHILD4"},
   503  			},
   504  			{
   505  				module:    "base4",
   506  				name:      "CHILD4",
   507  				baseNames: []string{"BASE4"},
   508  			},
   509  		},
   510  		idrefs: []idrefOut{
   511  			{
   512  				module: "base4",
   513  				name:   "tref",
   514  				values: []string{"CHILD4"},
   515  			},
   516  		},
   517  	},
   518  	{
   519  		name: "tree-test-case-5",
   520  		in: []inputModule{
   521  			{
   522  				name: "base.yang",
   523  				content: `
   524  					module base5 {
   525  						namespace "urn:base";
   526  						prefix "base5";
   527  
   528  						identity BASE5A;
   529  						identity BASE5B;
   530  
   531  						identity FIVE_ONE {
   532  							base BASE5A;
   533  						}
   534  
   535  						identity FIVE_TWO {
   536  							base BASE5B;
   537  						}
   538  
   539  						leaf union {
   540  							type union {
   541  								type identityref {
   542  									base BASE5A;
   543  								}
   544  								type identityref {
   545  									base BASE5B;
   546  								}
   547  							}
   548  						}
   549  					}`},
   550  		},
   551  		identities: []identityOut{
   552  			{
   553  				module: "base5",
   554  				name:   "BASE5A",
   555  				values: []string{"FIVE_ONE"},
   556  			},
   557  			{
   558  				module: "base5",
   559  				name:   "BASE5B",
   560  				values: []string{"FIVE_TWO"},
   561  			},
   562  		},
   563  		idrefs: []idrefOut{
   564  			{
   565  				module: "base5",
   566  				name:   "union",
   567  				values: []string{"FIVE_ONE", "FIVE_TWO"},
   568  			},
   569  		},
   570  	},
   571  	{
   572  		name: "identity's base can't be found",
   573  		in: []inputModule{
   574  			{
   575  				name: "idtest",
   576  				content: `
   577  					module idtest{
   578  					  namespace "urn:idtwo";
   579  					  prefix "idone";
   580  
   581  					  identity TEST_ID_TWO;
   582  					  identity TEST_CHILD {
   583  					    base TEST_ID;
   584  					  }
   585  					}
   586  				`},
   587  		},
   588  		identities: []identityOut{
   589  			{module: "idtest", name: "TEST_ID2"},
   590  		},
   591  		wantErrSubstr: "can't resolve the local base",
   592  	},
   593  	{
   594  		name: "identity's base can't be found in remote",
   595  		in: []inputModule{
   596  			{
   597  				name: "remote.yang",
   598  				content: `
   599  				  module remote {
   600  				    namespace "urn:remote";
   601  				    prefix "remote";
   602  
   603  				    identity REMOTE_BASE_ESCAPE;
   604  				  }
   605  				`},
   606  			{
   607  				name: "base.yang",
   608  				content: `
   609  				  module base {
   610  				    namespace "urn:base";
   611  				    prefix "base";
   612  
   613  				    import remote { prefix "r"; }
   614  
   615  				    identity LOCAL_REMOTE_BASE {
   616  				      base r:REMOTE_BASE;
   617  				    }
   618  				  }
   619  				`},
   620  		},
   621  		identities: []identityOut{
   622  			{module: "base", name: "LOCAL_REMOTE_BASE"},
   623  		},
   624  		wantErrSubstr: "can't resolve remote base",
   625  	},
   626  	{
   627  		name: "identity's base's module can't be found",
   628  		in: []inputModule{
   629  			{
   630  				name: "remote.yang",
   631  				content: `
   632  				  module remote {
   633  				    namespace "urn:remote";
   634  				    prefix "remote";
   635  
   636  				    identity REMOTE_BASE;
   637  				  }
   638  				`},
   639  			{
   640  				name: "base.yang",
   641  				content: `
   642  				  module base {
   643  				    namespace "urn:base";
   644  				    prefix "base";
   645  
   646  				    import remote { prefix "r"; }
   647  
   648  				    identity LOCAL_REMOTE_BASE {
   649  				      base roe:REMOTE_BASE;
   650  				    }
   651  				  }
   652  				`},
   653  		},
   654  		identities: []identityOut{
   655  			{module: "base", name: "LOCAL_REMOTE_BASE"},
   656  		},
   657  		wantErrSubstr: "can't find external module",
   658  	},
   659  }
   660  
   661  // TestIdentityTree - check inheritance of identities from local and remote
   662  // sources. The Values of an Identity correspond to the values that are
   663  // referenced by that identity, which need to be inherited.
   664  func TestIdentityTree(t *testing.T) {
   665  	for _, tt := range treeTestCases {
   666  		t.Run(tt.name, func(t *testing.T) {
   667  			ms := NewModules()
   668  
   669  			for _, mod := range tt.in {
   670  				_ = ms.Parse(mod.content, mod.name)
   671  			}
   672  
   673  			errs := ms.Process()
   674  
   675  			var err error
   676  			switch len(errs) {
   677  			case 1:
   678  				err = errs[0]
   679  				if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" {
   680  					t.Fatalf("%s", diff)
   681  				}
   682  				return
   683  			case 0:
   684  				if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" {
   685  					t.Fatalf("%s", diff)
   686  				}
   687  			default:
   688  				t.Fatalf("got multiple errors: %v", errs)
   689  			}
   690  
   691  			// Walk through the identities that are defined in the test case output
   692  			// and validate that they exist, and their base and values are as expected.
   693  			for _, chkID := range tt.identities {
   694  				m, errs := ms.GetModule(chkID.module)
   695  				if errs != nil {
   696  					t.Errorf("Couldn't find expected module: %v", errs)
   697  					continue
   698  				}
   699  
   700  				var foundID *Identity
   701  				for _, i := range m.Identities {
   702  					if i.Name == chkID.name {
   703  						foundID = i
   704  						break
   705  					}
   706  				}
   707  
   708  				if foundID == nil {
   709  					t.Errorf("Couldn't find identity %s in module %s", chkID.name,
   710  						chkID.module)
   711  					continue
   712  				}
   713  
   714  				if len(chkID.baseNames) > 0 {
   715  					actualBaseNames := getBaseNamesFrom(foundID)
   716  					if diff := cmp.Diff(actualBaseNames, chkID.baseNames); diff != "" {
   717  						t.Errorf("(-got, +want):\n%s", diff)
   718  					}
   719  				}
   720  
   721  				valueMap := make(map[string]bool)
   722  
   723  				for i, val := range chkID.values {
   724  					valueMap[val] = false
   725  					// Check that IsDefined returns the right result
   726  					if !foundID.IsDefined(val) {
   727  						t.Errorf("Couldn't find defined value %s  for %s", val, chkID.name)
   728  					}
   729  
   730  					// Check that the values are sorted in a consistent order
   731  					if foundID.Values[i].Name != val {
   732  						t.Errorf("Invalid order for value #%d. Expecting %s Got %s", i, foundID.Values[i].Name, val)
   733  					}
   734  					// Check that GetValue returns the right Identity
   735  					idval := foundID.GetValue(val)
   736  					if idval == nil {
   737  						t.Errorf("Couldn't GetValue(%s) for %s", val, chkID.name)
   738  					}
   739  				}
   740  
   741  				// Ensure that IsDefined does not return false positives
   742  				if foundID.IsDefined("DoesNotExist") {
   743  					t.Errorf("Non-existent value IsDefined for %s", foundID.Name)
   744  				}
   745  
   746  				if foundID.GetValue("DoesNotExist") != nil {
   747  					t.Errorf("Non-existent value GetValue not nil for %s", foundID.Name)
   748  				}
   749  
   750  				for _, chkv := range foundID.Values {
   751  					_, ok := valueMap[chkv.Name]
   752  					if !ok {
   753  						t.Errorf("Found unexpected value %s for %s", chkv.Name, chkID.name)
   754  						continue
   755  					}
   756  					valueMap[chkv.Name] = true
   757  				}
   758  
   759  				for k, v := range valueMap {
   760  					if v == false {
   761  						t.Errorf("Could not find identity %s for %s", k, chkID.name)
   762  					}
   763  				}
   764  			}
   765  
   766  			for _, idr := range tt.idrefs {
   767  				m, errs := ms.GetModule(idr.module)
   768  				if errs != nil {
   769  					t.Errorf("Couldn't find expected module %s: %v", idr.module, errs)
   770  					continue
   771  				}
   772  
   773  				if _, ok := m.Dir[idr.name]; !ok {
   774  					t.Errorf("Could not find expected identity, got: nil, want: %v", idr.name)
   775  					continue
   776  				}
   777  
   778  				identity := m.Dir[idr.name]
   779  				var vals []*Identity
   780  				switch len(identity.Type.Type) {
   781  				case 0:
   782  					vals = identity.Type.IdentityBase.Values
   783  				default:
   784  					for _, b := range identity.Type.Type {
   785  						if b.IdentityBase != nil {
   786  							vals = append(vals, b.IdentityBase.Values...)
   787  						}
   788  					}
   789  				}
   790  
   791  				var valNames []string
   792  				for _, v := range vals {
   793  					valNames = append(valNames, v.Name)
   794  				}
   795  
   796  				if diff := cmp.Diff(idr.values, valNames); diff != "" {
   797  					t.Errorf("Identity %s did not have expected values, (-got, +want):\n%s", idr.name, diff)
   798  				}
   799  			}
   800  		})
   801  	}
   802  }