github.com/bazelbuild/rules_webtesting@v0.2.0/go/metadata/capabilities/capabilities_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 capabilities
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"testing"
    21  )
    22  
    23  func TestMerge(t *testing.T) {
    24  	testCases := []struct {
    25  		name   string
    26  		input1 map[string]interface{}
    27  		input2 map[string]interface{}
    28  		result map[string]interface{}
    29  	}{
    30  		{
    31  			name:   "int,int",
    32  			input1: map[string]interface{}{"v": 1},
    33  			input2: map[string]interface{}{"v": 2},
    34  			result: map[string]interface{}{"v": 2},
    35  		},
    36  		{
    37  			name:   "string,bool",
    38  			input1: map[string]interface{}{"v": "a string"},
    39  			input2: map[string]interface{}{"v": true},
    40  			result: map[string]interface{}{"v": true},
    41  		},
    42  		{
    43  			name:   "int,string",
    44  			input1: map[string]interface{}{"v": 1},
    45  			input2: map[string]interface{}{"v": "a string"},
    46  			result: map[string]interface{}{"v": "a string"},
    47  		},
    48  		{
    49  			name:   "int,slice",
    50  			input1: map[string]interface{}{"v": 1},
    51  			input2: map[string]interface{}{"v": []interface{}{"string 1", 2, true, nil}},
    52  			result: map[string]interface{}{"v": []interface{}{"string 1", 2, true, nil}},
    53  		},
    54  		{
    55  			name:   "slice,slice",
    56  			input1: map[string]interface{}{"v": []interface{}{"string 1", "string 2"}},
    57  			input2: map[string]interface{}{"v": []interface{}{1, 2}},
    58  			result: map[string]interface{}{"v": []interface{}{"string 1", "string 2", 1, 2}},
    59  		},
    60  		{
    61  			name:   "int,map",
    62  			input1: map[string]interface{}{"v": 1},
    63  			input2: map[string]interface{}{"v": map[string]interface{}{
    64  				"1": 1,
    65  				"2": 2,
    66  			}},
    67  			result: map[string]interface{}{"v": map[string]interface{}{
    68  				"1": 1,
    69  				"2": 2,
    70  			}},
    71  		},
    72  		{
    73  			name: "map,map",
    74  			input1: map[string]interface{}{"v": map[string]interface{}{
    75  				"1": 1,
    76  				"2": 2,
    77  			}},
    78  			input2: map[string]interface{}{"v": map[string]interface{}{
    79  				"2": 3,
    80  				"3": 4,
    81  			}},
    82  			result: map[string]interface{}{"v": map[string]interface{}{"1": 1, "2": 3, "3": 4}},
    83  		},
    84  		{
    85  			name: "mixed",
    86  			input1: map[string]interface{}{
    87  				"1": []interface{}{1, 2},
    88  				"2": map[string]interface{}{"a": "an a", "b": "a b", "c": "a c"},
    89  				"3": 3,
    90  			},
    91  			input2: map[string]interface{}{
    92  				"1": []interface{}{"a"},
    93  				"2": map[string]interface{}{"a": "a c", "b": "a d"},
    94  			},
    95  			result: map[string]interface{}{
    96  				"1": []interface{}{1, 2, "a"},
    97  				"2": map[string]interface{}{"a": "a c", "b": "a d", "c": "a c"},
    98  				"3": 3,
    99  			},
   100  		},
   101  		{
   102  			name: "args -- no redefines",
   103  			input1: map[string]interface{}{
   104  				"args": []interface{}{
   105  					"an option",
   106  					"--anOption",
   107  					"--anOption=true",
   108  					"-anotherOption",
   109  					map[string]interface{}{
   110  						"some": "map",
   111  					},
   112  				},
   113  			},
   114  			input2: map[string]interface{}{
   115  				"args": []interface{}{
   116  					"an option",
   117  					"anOption",
   118  					"-anOption=true",
   119  					"-anotherOption",
   120  					map[string]interface{}{
   121  						"some": "map",
   122  					},
   123  				},
   124  			},
   125  			result: map[string]interface{}{
   126  				"args": []interface{}{
   127  					"an option",
   128  					"--anOption",
   129  					"--anOption=true",
   130  					"-anotherOption",
   131  					map[string]interface{}{
   132  						"some": "map",
   133  					},
   134  					"an option",
   135  					"anOption",
   136  					"-anOption=true",
   137  					"-anotherOption",
   138  					map[string]interface{}{
   139  						"some": "map",
   140  					},
   141  				},
   142  			},
   143  		},
   144  		{
   145  			name: "args -- redefines",
   146  			input1: map[string]interface{}{
   147  				"args": []interface{}{
   148  					"an option",
   149  					"--anOption",
   150  					"--anOption=true",
   151  					"--optionToLeave=this",
   152  					map[string]interface{}{
   153  						"some": "map",
   154  					},
   155  				},
   156  			},
   157  			input2: map[string]interface{}{
   158  				"args": []interface{}{
   159  					"an option",
   160  					"--anOption=false",
   161  					"--anotherOption",
   162  					"-optionToLeave=that",
   163  					map[string]interface{}{
   164  						"some": "map",
   165  					},
   166  				},
   167  			},
   168  			result: map[string]interface{}{
   169  				"args": []interface{}{
   170  					"an option",
   171  					"--optionToLeave=this",
   172  					map[string]interface{}{
   173  						"some": "map",
   174  					},
   175  					"an option",
   176  					"--anOption=false",
   177  					"--anotherOption",
   178  					"-optionToLeave=that",
   179  					map[string]interface{}{
   180  						"some": "map",
   181  					},
   182  				},
   183  			},
   184  		},
   185  	}
   186  
   187  	for _, tc := range testCases {
   188  		t.Run(tc.name, func(t *testing.T) {
   189  			if result := Merge(tc.input1, tc.input2); !reflect.DeepEqual(tc.result, result) {
   190  				t.Errorf("Got Merge(%+v, %+v) == %+v, expected %+v", tc.input1, tc.input2, result, tc.result)
   191  			}
   192  		})
   193  	}
   194  }
   195  
   196  func TestFromNewSessionArgs(t *testing.T) {
   197  	testCases := []struct {
   198  		name    string
   199  		args    map[string]interface{}
   200  		want    *Capabilities
   201  		wantErr bool
   202  	}{
   203  		{
   204  			name: "empty args",
   205  			args: map[string]interface{}{},
   206  			want: &Capabilities{
   207  				AlwaysMatch:  map[string]interface{}{},
   208  				W3CSupported: false,
   209  			},
   210  			wantErr: false,
   211  		},
   212  		{
   213  			name: "alwaysMatch",
   214  			args: map[string]interface{}{
   215  				"capabilities": map[string]interface{}{
   216  					"alwaysMatch": map[string]interface{}{
   217  						"key1": "value1",
   218  					},
   219  				},
   220  			},
   221  			want: &Capabilities{
   222  				AlwaysMatch: map[string]interface{}{
   223  					"key1": "value1",
   224  				},
   225  				W3CSupported: true,
   226  			},
   227  			wantErr: false,
   228  		},
   229  		{
   230  			name: "requiredCapabilities",
   231  			args: map[string]interface{}{
   232  				"requiredCapabilities": map[string]interface{}{
   233  					"key1": "value1",
   234  				},
   235  			},
   236  			want: &Capabilities{
   237  				AlwaysMatch: map[string]interface{}{
   238  					"key1": "value1",
   239  				},
   240  				W3CSupported: false,
   241  			},
   242  			wantErr: false,
   243  		},
   244  		{
   245  			name: "desiredCapabilities",
   246  			args: map[string]interface{}{
   247  				"desiredCapabilities": map[string]interface{}{
   248  					"key1": "value1",
   249  				},
   250  			},
   251  			want: &Capabilities{
   252  				AlwaysMatch: map[string]interface{}{
   253  					"key1": "value1",
   254  				},
   255  				W3CSupported: false,
   256  			},
   257  			wantErr: false,
   258  		},
   259  		{
   260  			name: "all three",
   261  			args: map[string]interface{}{
   262  				"capabilities": map[string]interface{}{
   263  					"alwaysMatch": map[string]interface{}{
   264  						"key1": "value1",
   265  					},
   266  				},
   267  				"desiredCapabilities": map[string]interface{}{
   268  					"key2": "value2",
   269  				},
   270  				"requiredCapabilities": map[string]interface{}{
   271  					"key3": "value3",
   272  				},
   273  			},
   274  			want: &Capabilities{
   275  				AlwaysMatch: map[string]interface{}{
   276  					"key1": "value1",
   277  					"key2": "value2",
   278  					"key3": "value3",
   279  				},
   280  				W3CSupported: true,
   281  			},
   282  			wantErr: false,
   283  		},
   284  		{
   285  			name: "all three, same value ok",
   286  			args: map[string]interface{}{
   287  				"capabilities": map[string]interface{}{
   288  					"alwaysMatch": map[string]interface{}{
   289  						"key1": "value1",
   290  					},
   291  				},
   292  				"desiredCapabilities": map[string]interface{}{
   293  					"key1": "value1",
   294  				},
   295  				"requiredCapabilities": map[string]interface{}{
   296  					"key1": "value1",
   297  				},
   298  			},
   299  			want: &Capabilities{
   300  				AlwaysMatch: map[string]interface{}{
   301  					"key1": "value1",
   302  				},
   303  				W3CSupported: true,
   304  			},
   305  			wantErr: false,
   306  		},
   307  		{
   308  			name: "always, required != desired",
   309  			args: map[string]interface{}{
   310  				"capabilities": map[string]interface{}{
   311  					"alwaysMatch": map[string]interface{}{
   312  						"key1": "value1",
   313  					},
   314  				},
   315  				"desiredCapabilities": map[string]interface{}{
   316  					"key1": "value12",
   317  				},
   318  				"requiredCapabilities": map[string]interface{}{
   319  					"key1": "value1",
   320  				},
   321  			},
   322  			want:    nil,
   323  			wantErr: true,
   324  		},
   325  		{
   326  			name: "always, desired != required",
   327  			args: map[string]interface{}{
   328  				"capabilities": map[string]interface{}{
   329  					"alwaysMatch": map[string]interface{}{
   330  						"key1": "value1",
   331  					},
   332  				},
   333  				"desiredCapabilities": map[string]interface{}{
   334  					"key1": "value1",
   335  				},
   336  				"requiredCapabilities": map[string]interface{}{
   337  					"key1": "value12",
   338  				},
   339  			},
   340  			want:    nil,
   341  			wantErr: true,
   342  		},
   343  		{
   344  			name: "always != desired. required",
   345  			args: map[string]interface{}{
   346  				"capabilities": map[string]interface{}{
   347  					"alwaysMatch": map[string]interface{}{
   348  						"key1": "value12",
   349  					},
   350  				},
   351  				"desiredCapabilities": map[string]interface{}{
   352  					"key1": "value1",
   353  				},
   354  				"requiredCapabilities": map[string]interface{}{
   355  					"key1": "value1",
   356  				},
   357  			},
   358  			want:    nil,
   359  			wantErr: true,
   360  		},
   361  		{
   362  			name: "firstMatch, no conflicts",
   363  			args: map[string]interface{}{
   364  				"capabilities": map[string]interface{}{
   365  					"alwaysMatch": map[string]interface{}{
   366  						"key1": "value1",
   367  					},
   368  					"firstMatch": []interface{}{
   369  						map[string]interface{}{
   370  							"key2": "value2",
   371  						},
   372  						map[string]interface{}{
   373  							"key2": "value3",
   374  						},
   375  					},
   376  				},
   377  			},
   378  			want: &Capabilities{
   379  				AlwaysMatch: map[string]interface{}{
   380  					"key1": "value1",
   381  				},
   382  				FirstMatch: []map[string]interface{}{
   383  					{
   384  						"key2": "value2",
   385  					},
   386  					{
   387  						"key2": "value3",
   388  					},
   389  				},
   390  				W3CSupported: true,
   391  			},
   392  			wantErr: false,
   393  		},
   394  		{
   395  			name: "firstMatch, same value as alwaysMatch",
   396  			args: map[string]interface{}{
   397  				"capabilities": map[string]interface{}{
   398  					"alwaysMatch": map[string]interface{}{
   399  						"key1": "value1",
   400  					},
   401  					"firstMatch": []interface{}{
   402  						map[string]interface{}{
   403  							"key1": "value1",
   404  							"key2": "value2",
   405  						},
   406  						map[string]interface{}{
   407  							"key2": "value3",
   408  						},
   409  					},
   410  				},
   411  			},
   412  			want: &Capabilities{
   413  				AlwaysMatch: map[string]interface{}{
   414  					"key1": "value1",
   415  				},
   416  				FirstMatch: []map[string]interface{}{
   417  					{
   418  						"key2": "value2",
   419  					},
   420  					{
   421  						"key2": "value3",
   422  					},
   423  				},
   424  				W3CSupported: true,
   425  			},
   426  			wantErr: false,
   427  		},
   428  		{
   429  			name: "firstMatch, different value than alwaysMatch",
   430  			args: map[string]interface{}{
   431  				"capabilities": map[string]interface{}{
   432  					"alwaysMatch": map[string]interface{}{
   433  						"key1": "value1",
   434  					},
   435  					"firstMatch": []interface{}{
   436  						map[string]interface{}{
   437  							"key1": "value12",
   438  							"key2": "value2",
   439  						},
   440  						map[string]interface{}{
   441  							"key2": "value3",
   442  						},
   443  					},
   444  				},
   445  			},
   446  			want:    nil,
   447  			wantErr: true,
   448  		},
   449  	}
   450  
   451  	for _, tc := range testCases {
   452  		t.Run(tc.name, func(t *testing.T) {
   453  			got, err := FromNewSessionArgs(tc.args)
   454  
   455  			if err != nil || tc.wantErr {
   456  				if (err != nil) != tc.wantErr {
   457  					t.Fatalf("got err %v, wantErr==%t", err, tc.wantErr)
   458  				}
   459  				return
   460  			}
   461  
   462  			if !reflect.DeepEqual(got, tc.want) {
   463  				t.Fatalf("got %#v, want %#v", got, tc.want)
   464  			}
   465  		})
   466  	}
   467  }
   468  
   469  func TestMergeOver(t *testing.T) {
   470  	testCases := []struct {
   471  		name  string
   472  		this  *Capabilities
   473  		other map[string]interface{}
   474  		want  *Capabilities
   475  	}{
   476  		{
   477  			name: "empty",
   478  			this: &Capabilities{
   479  				AlwaysMatch: map[string]interface{}{},
   480  			},
   481  			other: map[string]interface{}{},
   482  			want: &Capabilities{
   483  				AlwaysMatch: map[string]interface{}{},
   484  			},
   485  		},
   486  		{
   487  			name: "no overlap",
   488  			this: &Capabilities{
   489  				AlwaysMatch: map[string]interface{}{
   490  					"key1": "value1",
   491  				},
   492  				FirstMatch: []map[string]interface{}{
   493  					{
   494  						"key2": "value2",
   495  					},
   496  					{
   497  						"key3": "value3",
   498  					},
   499  				},
   500  			},
   501  			other: map[string]interface{}{
   502  				"key4": "value4",
   503  			},
   504  			want: &Capabilities{
   505  				AlwaysMatch: map[string]interface{}{
   506  					"key1": "value1",
   507  					"key4": "value4",
   508  				},
   509  				FirstMatch: []map[string]interface{}{
   510  					{
   511  						"key2": "value2",
   512  					},
   513  					{
   514  						"key3": "value3",
   515  					},
   516  				},
   517  			},
   518  		},
   519  		{
   520  			name: "overlaps always",
   521  			this: &Capabilities{
   522  				AlwaysMatch: map[string]interface{}{
   523  					"key1": "value1",
   524  				},
   525  				FirstMatch: []map[string]interface{}{
   526  					{
   527  						"key2": "value2",
   528  					},
   529  					{
   530  						"key3": "value3",
   531  					},
   532  				},
   533  			},
   534  			other: map[string]interface{}{
   535  				"key1": "value4",
   536  			},
   537  			want: &Capabilities{
   538  				AlwaysMatch: map[string]interface{}{
   539  					"key1": "value1",
   540  				},
   541  				FirstMatch: []map[string]interface{}{
   542  					{
   543  						"key2": "value2",
   544  					},
   545  					{
   546  						"key3": "value3",
   547  					},
   548  				},
   549  			},
   550  		},
   551  		{
   552  			name: "overlaps first[0]",
   553  			this: &Capabilities{
   554  				AlwaysMatch: map[string]interface{}{
   555  					"key1": "value1",
   556  				},
   557  				FirstMatch: []map[string]interface{}{
   558  					{
   559  						"key2": "value2",
   560  					},
   561  					{
   562  						"key3": "value3",
   563  					},
   564  				},
   565  			},
   566  			other: map[string]interface{}{
   567  				"key2": "value4",
   568  			},
   569  			want: &Capabilities{
   570  				AlwaysMatch: map[string]interface{}{
   571  					"key1": "value1",
   572  				},
   573  				FirstMatch: []map[string]interface{}{
   574  					{
   575  						"key2": "value2",
   576  					},
   577  					{
   578  						"key2": "value4",
   579  						"key3": "value3",
   580  					},
   581  				},
   582  			},
   583  		},
   584  		{
   585  			name: "overlaps first[1]",
   586  			this: &Capabilities{
   587  				AlwaysMatch: map[string]interface{}{
   588  					"key1": "value1",
   589  				},
   590  				FirstMatch: []map[string]interface{}{
   591  					{
   592  						"key2": "value2",
   593  					},
   594  					{
   595  						"key3": "value3",
   596  					},
   597  				},
   598  			},
   599  			other: map[string]interface{}{
   600  				"key3": "value4",
   601  			},
   602  			want: &Capabilities{
   603  				AlwaysMatch: map[string]interface{}{
   604  					"key1": "value1",
   605  				},
   606  				FirstMatch: []map[string]interface{}{
   607  					{
   608  						"key2": "value2",
   609  						"key3": "value4",
   610  					},
   611  					{
   612  						"key3": "value3",
   613  					},
   614  				},
   615  			},
   616  		},
   617  		{
   618  			name: "overlap and non-overlap",
   619  			this: &Capabilities{
   620  				AlwaysMatch: map[string]interface{}{
   621  					"key1": "value1",
   622  				},
   623  				FirstMatch: []map[string]interface{}{
   624  					{
   625  						"key2": "value2",
   626  					},
   627  					{
   628  						"key3": "value3",
   629  					},
   630  				},
   631  			},
   632  			other: map[string]interface{}{
   633  				"key1": "value11",
   634  				"key2": "value22",
   635  				"key3": "value33",
   636  				"key4": "value4",
   637  			},
   638  			want: &Capabilities{
   639  				AlwaysMatch: map[string]interface{}{
   640  					"key1": "value1",
   641  					"key4": "value4",
   642  				},
   643  				FirstMatch: []map[string]interface{}{
   644  					{
   645  						"key2": "value2",
   646  						"key3": "value33",
   647  					},
   648  					{
   649  						"key2": "value22",
   650  						"key3": "value3",
   651  					},
   652  				},
   653  			},
   654  		},
   655  	}
   656  
   657  	for _, tc := range testCases {
   658  		t.Run(tc.name, func(t *testing.T) {
   659  			got := tc.this.MergeOver(tc.other)
   660  			if !reflect.DeepEqual(got, tc.want) {
   661  				t.Fatalf("got %#v, want %#v", got, tc.want)
   662  			}
   663  		})
   664  	}
   665  }
   666  
   667  func TestResolve(t *testing.T) {
   668  	testCases := []struct {
   669  		name     string
   670  		in       *Capabilities
   671  		resolver Resolver
   672  		out      *Capabilities
   673  		err      bool
   674  	}{
   675  		{
   676  			name: "empty capabilities",
   677  			in: &Capabilities{
   678  				AlwaysMatch: map[string]interface{}{},
   679  			},
   680  			resolver: func(prefix, name string) (string, error) {
   681  				return "", fmt.Errorf("resolver called with %s:%s", prefix, name)
   682  			},
   683  			out: &Capabilities{
   684  				AlwaysMatch: map[string]interface{}{},
   685  			},
   686  			err: false,
   687  		},
   688  		{
   689  			name: "NoOP resolver",
   690  			in: &Capabilities{
   691  				AlwaysMatch: map[string]interface{}{
   692  					"abc": "%p:n%",
   693  				},
   694  				FirstMatch: []map[string]interface{}{
   695  					{
   696  						"xyz": "%n:p%",
   697  					},
   698  				},
   699  			},
   700  			resolver: NoOPResolver,
   701  			out: &Capabilities{
   702  				AlwaysMatch: map[string]interface{}{
   703  					"abc": "%p:n%",
   704  				},
   705  				FirstMatch: []map[string]interface{}{
   706  					{
   707  						"xyz": "%n:p%",
   708  					},
   709  				},
   710  			},
   711  			err: false,
   712  		},
   713  		{
   714  			name: "MapResolver",
   715  			in: &Capabilities{
   716  				AlwaysMatch: map[string]interface{}{
   717  					"abc": "%p:n%",
   718  				},
   719  				FirstMatch: []map[string]interface{}{
   720  					{
   721  						"xyz": "%n:p%",
   722  					},
   723  				},
   724  			},
   725  			resolver: MapResolver("p", map[string]string{"n": "some value"}),
   726  			out: &Capabilities{
   727  				AlwaysMatch: map[string]interface{}{
   728  					"abc": "some value",
   729  				},
   730  				FirstMatch: []map[string]interface{}{
   731  					{
   732  						"xyz": "%n:p%",
   733  					},
   734  				},
   735  			},
   736  			err: false,
   737  		},
   738  		{
   739  			name: "complex input",
   740  			in: &Capabilities{
   741  				AlwaysMatch: map[string]interface{}{
   742  					"abc": []interface{}{"%p:n%"},
   743  				},
   744  				FirstMatch: []map[string]interface{}{
   745  					{
   746  						"xyz": map[string]interface{}{"zyx": "%n:p%=%p:n%"},
   747  					},
   748  				},
   749  			},
   750  			resolver: func(prefix, name string) (string, error) {
   751  				if prefix == "p" && name == "n" {
   752  					return "some-value", nil
   753  				}
   754  				if prefix == "n" && name == "p" {
   755  					return "value-some", nil
   756  				}
   757  				return "", fmt.Errorf("unknown %s:%s", prefix, name)
   758  			},
   759  			out: &Capabilities{
   760  				AlwaysMatch: map[string]interface{}{
   761  					"abc": []interface{}{"some-value"},
   762  				},
   763  				FirstMatch: []map[string]interface{}{
   764  					{
   765  						"xyz": map[string]interface{}{"zyx": "value-some=some-value"},
   766  					},
   767  				},
   768  			},
   769  			err: false,
   770  		},
   771  		{
   772  			name: "resolver returns error",
   773  			in: &Capabilities{
   774  				AlwaysMatch: map[string]interface{}{
   775  					"abc": []interface{}{"%p:n%"},
   776  				},
   777  				FirstMatch: []map[string]interface{}{
   778  					{
   779  						"xyz": map[string]interface{}{"zyx": "%n:p%=%p:n%"},
   780  					},
   781  					{
   782  						"bad": "%x:y%",
   783  					},
   784  				},
   785  			},
   786  			resolver: func(prefix, name string) (string, error) {
   787  				if prefix == "p" && name == "n" {
   788  					return "some-value", nil
   789  				}
   790  				if prefix == "n" && name == "p" {
   791  					return "value-some", nil
   792  				}
   793  				return "", fmt.Errorf("unknown %s:%s", prefix, name)
   794  			},
   795  			out: nil,
   796  			err: true,
   797  		},
   798  	}
   799  
   800  	for _, tc := range testCases {
   801  		t.Run(tc.name, func(t *testing.T) {
   802  			out, err := tc.in.Resolve(tc.resolver)
   803  
   804  			if err != nil {
   805  				if !tc.err {
   806  					t.Fatal(err)
   807  				}
   808  				return
   809  			}
   810  			if tc.err {
   811  				t.Fatalf("got nil err, want err")
   812  			}
   813  
   814  			if !reflect.DeepEqual(out, tc.out) {
   815  				t.Fatalf("got %#v, want %#v", out, tc.out)
   816  			}
   817  		})
   818  	}
   819  }