github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/query/atoms_test.go (about)

     1  //go:build small
     2  // +build small
     3  
     4  // Copyright 2018 The WPT Dashboard Project. All rights reserved.
     5  // Use of this source code is governed by a BSD-style license that can be
     6  // found in the LICENSE file.
     7  
     8  package query
     9  
    10  import (
    11  	"encoding/json"
    12  	"testing"
    13  
    14  	"github.com/golang/mock/gomock"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/web-platform-tests/wpt.fyi/shared"
    17  	"github.com/web-platform-tests/wpt.fyi/shared/sharedtest"
    18  )
    19  
    20  func TestStructuredQuery_empty(t *testing.T) {
    21  	var rq RunQuery
    22  	err := json.Unmarshal([]byte(`{}`), &rq)
    23  	assert.NotNil(t, err)
    24  }
    25  
    26  func TestStructuredQuery_missingRunIDs(t *testing.T) {
    27  	var rq RunQuery
    28  	err := json.Unmarshal([]byte(`{
    29  		"query": {
    30  			"pattern": "/2dcontext/"
    31  		}
    32  	}`), &rq)
    33  	assert.NotNil(t, err)
    34  }
    35  
    36  func TestStructuredQuery_missingQuery(t *testing.T) {
    37  	var rq RunQuery
    38  	err := json.Unmarshal([]byte(`{
    39  		"run_ids": [0, 1, 2]
    40  	}`), &rq)
    41  	assert.Nil(t, err)
    42  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: True{}}, rq)
    43  }
    44  
    45  func TestStructuredQuery_emptyRunIDs(t *testing.T) {
    46  	var rq RunQuery
    47  	err := json.Unmarshal([]byte(`{
    48  		"run_ids": [],
    49  		"query": {
    50  			"pattern": "/2dcontext/"
    51  		}
    52  	}`), &rq)
    53  	assert.NotNil(t, err)
    54  }
    55  
    56  func TestStructuredQuery_emptyBrowserName(t *testing.T) {
    57  	var rq RunQuery
    58  	err := json.Unmarshal([]byte(`{
    59  		"run_ids": [0, 1, 2],
    60  		"query": {
    61  			"status": "PASS"
    62  		}
    63  	}`), &rq)
    64  	assert.Nil(t, err)
    65  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: TestStatusEq{Status: shared.TestStatusValueFromString("PASS")}}, rq)
    66  }
    67  
    68  func TestStructuredQuery_missingStatus(t *testing.T) {
    69  	var rq RunQuery
    70  	err := json.Unmarshal([]byte(`{
    71  		"run_ids": [0, 1, 2],
    72  		"query": {
    73  			"product": "chrome"
    74  		}
    75  	}`), &rq)
    76  	assert.NotNil(t, err)
    77  }
    78  
    79  func TestStructuredQuery_badStatus(t *testing.T) {
    80  	var rq RunQuery
    81  	err := json.Unmarshal([]byte(`{
    82  		"run_ids": [0, 1, 2],
    83  		"query": {
    84  			"product": "chrome",
    85  			"status": "NOT_A_REAL_STATUS"
    86  		}
    87  	}`), &rq)
    88  	assert.NotNil(t, err)
    89  }
    90  func TestStructuredQuery_unknownStatus(t *testing.T) {
    91  	var rq RunQuery
    92  	err := json.Unmarshal([]byte(`{
    93  		"run_ids": [0, 1, 2],
    94  		"query": {
    95  			"product": "chrome",
    96  			"status": "UNKNOWN"
    97  		}
    98  	}`), &rq)
    99  	assert.Nil(t, err)
   100  	p := shared.ParseProductSpecUnsafe("chrome")
   101  	assert.Equal(t, RunQuery{
   102  		RunIDs:        []int64{0, 1, 2},
   103  		AbstractQuery: TestStatusEq{&p, shared.TestStatusValueFromString("UNKNOWN")},
   104  	}, rq)
   105  }
   106  
   107  func TestStructuredQuery_missingPattern(t *testing.T) {
   108  	var rq RunQuery
   109  	err := json.Unmarshal([]byte(`{
   110  		"run_ids": [0, 1, 2],
   111  		"query": {}
   112  	}`), &rq)
   113  	assert.NotNil(t, err)
   114  }
   115  
   116  func TestStructuredQuery_emptyPattern(t *testing.T) {
   117  	var rq RunQuery
   118  	err := json.Unmarshal([]byte(`{
   119  		"run_ids": [0, 1, 2],
   120  		"query": {
   121  			"pattern": ""
   122  		}
   123  	}`), &rq)
   124  	assert.Nil(t, err)
   125  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: TestNamePattern{""}}, rq)
   126  }
   127  
   128  func TestStructuredQuery_pattern(t *testing.T) {
   129  	var rq RunQuery
   130  	err := json.Unmarshal([]byte(`{
   131  		"run_ids": [0, 1, 2],
   132  		"query": {
   133  			"pattern": "/2dcontext/"
   134  		}
   135  	}`), &rq)
   136  	assert.Nil(t, err)
   137  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: TestNamePattern{"/2dcontext/"}}, rq)
   138  }
   139  
   140  func TestStructuredQuery_subtest(t *testing.T) {
   141  	var rq RunQuery
   142  	err := json.Unmarshal([]byte(`{
   143  		"run_ids": [0, 1, 2],
   144  		"query": {
   145  			"subtest": "Subtest name"
   146  		}
   147  	}`), &rq)
   148  	assert.Nil(t, err)
   149  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: SubtestNamePattern{"Subtest name"}}, rq)
   150  }
   151  
   152  func TestStructuredQuery_path(t *testing.T) {
   153  	var rq RunQuery
   154  	err := json.Unmarshal([]byte(`{
   155  		"run_ids": [0, 1, 2],
   156  		"query": {
   157  			"path": "/2dcontext/"
   158  		}
   159  	}`), &rq)
   160  	assert.Nil(t, err)
   161  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: TestPath{"/2dcontext/"}}, rq)
   162  }
   163  
   164  func TestStructuredQuery_legacyBrowserName(t *testing.T) {
   165  	var rq RunQuery
   166  	err := json.Unmarshal([]byte(`{
   167  		"run_ids": [0, 1, 2],
   168  		"query": {
   169  			"browser_name": "FiReFoX",
   170  			"status": "PaSs"
   171  		}
   172  	}`), &rq)
   173  	assert.Nil(t, err)
   174  	p := shared.ParseProductSpecUnsafe("firefox")
   175  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   176  		AbstractQuery: TestStatusEq{&p, shared.TestStatusValueFromString("PASS")},
   177  	}, rq)
   178  }
   179  
   180  func TestStructuredQuery_status(t *testing.T) {
   181  	var rq RunQuery
   182  	err := json.Unmarshal([]byte(`{
   183  		"run_ids": [0, 1, 2],
   184  		"query": {
   185  			"product": "FiReFoX",
   186  			"status": "PaSs"
   187  		}
   188  	}`), &rq)
   189  	assert.Nil(t, err)
   190  	p := shared.ParseProductSpecUnsafe("firefox")
   191  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   192  		AbstractQuery: TestStatusEq{&p, shared.TestStatusValueFromString("PASS")},
   193  	}, rq)
   194  }
   195  
   196  func TestStructuredQuery_statusNeq(t *testing.T) {
   197  	var rq RunQuery
   198  	err := json.Unmarshal([]byte(`{
   199  		"run_ids": [0, 1, 2],
   200  		"query": {
   201  			"product": "FiReFoX",
   202  			"status": {"not": "PaSs"}
   203  		}
   204  	}`), &rq)
   205  	assert.Nil(t, err)
   206  	p := shared.ParseProductSpecUnsafe("firefox")
   207  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   208  		AbstractQuery: TestStatusNeq{&p, shared.TestStatusValueFromString("PASS")},
   209  	}, rq)
   210  }
   211  
   212  func TestStructuredQuery_statusUnsupportedAbstractNot(t *testing.T) {
   213  	var rq RunQuery
   214  	err := json.Unmarshal([]byte(`{
   215  		"run_ids": [0, 1, 2],
   216  		"query": {
   217  			"product": "FiReFoX",
   218  			"status": {"not": {"pattern": "cssom"}}
   219  		}
   220  	}`), &rq)
   221  	assert.NotNil(t, err)
   222  }
   223  
   224  func TestStructuredQuery_not(t *testing.T) {
   225  	var rq RunQuery
   226  	err := json.Unmarshal([]byte(`{
   227  		"run_ids": [0, 1, 2],
   228  		"query": {
   229  			"not": {
   230  				"pattern": "cssom"
   231  			}
   232  		}
   233  	}`), &rq)
   234  	assert.Nil(t, err)
   235  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractNot{TestNamePattern{"cssom"}}}, rq)
   236  }
   237  
   238  func TestStructuredQuery_or(t *testing.T) {
   239  	var rq RunQuery
   240  	err := json.Unmarshal([]byte(`{
   241  		"run_ids": [0, 1, 2],
   242  		"query": {
   243  			"or": [
   244  				{"pattern": "cssom"},
   245  				{"pattern": "html"}
   246  			]
   247  		}
   248  	}`), &rq)
   249  	assert.Nil(t, err)
   250  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractOr{[]AbstractQuery{TestNamePattern{"cssom"}, TestNamePattern{"html"}}}}, rq)
   251  }
   252  
   253  func TestStructuredQuery_and(t *testing.T) {
   254  	var rq RunQuery
   255  	err := json.Unmarshal([]byte(`{
   256  		"run_ids": [0, 1, 2],
   257  		"query": {
   258  			"and": [
   259  				{"pattern": "cssom"},
   260  				{"pattern": "html"}
   261  			]
   262  		}
   263  	}`), &rq)
   264  	assert.Nil(t, err)
   265  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractAnd{[]AbstractQuery{TestNamePattern{"cssom"}, TestNamePattern{"html"}}}}, rq)
   266  }
   267  
   268  func TestStructuredQuery_exists(t *testing.T) {
   269  	var rq RunQuery
   270  	err := json.Unmarshal([]byte(`{
   271  		"run_ids": [0, 1, 2],
   272  		"query": {
   273  			"exists": [
   274  				{"pattern": "cssom"},
   275  				{"pattern": "html"}
   276  			]
   277  		}
   278  	}`), &rq)
   279  	assert.Nil(t, err)
   280  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractExists{[]AbstractQuery{TestNamePattern{"cssom"}, TestNamePattern{"html"}}}}, rq)
   281  }
   282  
   283  func TestStructuredQuery_all(t *testing.T) {
   284  	var rq RunQuery
   285  	err := json.Unmarshal([]byte(`{
   286  		"run_ids": [0, 1, 2],
   287  		"query": {
   288  			"all": [
   289  				{"pattern": "cssom"}
   290  			]
   291  		}
   292  	}`), &rq)
   293  	assert.Nil(t, err)
   294  	assert.Equal(t, RunQuery{
   295  		RunIDs:        []int64{0, 1, 2},
   296  		AbstractQuery: AbstractAll{[]AbstractQuery{TestNamePattern{"cssom"}}},
   297  	}, rq)
   298  }
   299  
   300  func TestStructuredQuery_none(t *testing.T) {
   301  	var rq RunQuery
   302  	err := json.Unmarshal([]byte(`{
   303  		"run_ids": [0, 1, 2],
   304  		"query": {
   305  			"none": [
   306  				{"pattern": "cssom"}
   307  			]
   308  		}
   309  	}`), &rq)
   310  	assert.Nil(t, err)
   311  	assert.Equal(t, RunQuery{
   312  		RunIDs:        []int64{0, 1, 2},
   313  		AbstractQuery: AbstractNone{[]AbstractQuery{TestNamePattern{"cssom"}}},
   314  	}, rq)
   315  }
   316  
   317  func TestStructuredQuery_sequential(t *testing.T) {
   318  	var rq RunQuery
   319  	err := json.Unmarshal([]byte(`{
   320  		"run_ids": [0, 1, 2],
   321  		"query": {
   322  			"exists": [
   323  				{ "sequential":[
   324  					{"or":[{"status":"PASS"},{"status":"OK"}]},
   325  					{"and":[{"status":{"not":"PASS"}},{"status":{"not":"OK"}},{"status":{"not":"UNKNOWN"}}]}
   326  				]}
   327  			]
   328  		}
   329  	}`), &rq)
   330  	assert.Nil(t, err)
   331  	assert.Equal(
   332  		t,
   333  		RunQuery{RunIDs: []int64{0, 1, 2},
   334  			AbstractQuery: AbstractExists{[]AbstractQuery{
   335  				AbstractSequential{[]AbstractQuery{
   336  					AbstractOr{[]AbstractQuery{
   337  						TestStatusEq{Status: shared.TestStatusValueFromString("PASS")},
   338  						TestStatusEq{Status: shared.TestStatusValueFromString("OK")},
   339  					}},
   340  					AbstractAnd{[]AbstractQuery{
   341  						TestStatusNeq{Status: shared.TestStatusValueFromString("PASS")},
   342  						TestStatusNeq{Status: shared.TestStatusValueFromString("OK")},
   343  						TestStatusNeq{Status: shared.TestStatusValueFromString("UNKNOWN")},
   344  					}},
   345  				}},
   346  			}}}, rq)
   347  }
   348  
   349  func TestStructuredQuery_count(t *testing.T) {
   350  	var rq RunQuery
   351  	err := json.Unmarshal([]byte(`{
   352  		"run_ids": [0, 1, 2],
   353  		"query": {
   354  			"exists": [{
   355  				"count": 3,
   356  				"where": {
   357  					"or": [{"status":"PASS"},{"status":"OK"}]
   358  				}
   359  			}]
   360  		}
   361  	}`), &rq)
   362  	assert.Nil(t, err)
   363  	assert.Equal(
   364  		t,
   365  		RunQuery{RunIDs: []int64{0, 1, 2},
   366  			AbstractQuery: AbstractExists{[]AbstractQuery{
   367  				AbstractCount{
   368  					Count: 3,
   369  					Where: AbstractOr{[]AbstractQuery{
   370  						TestStatusEq{Status: shared.TestStatusValueFromString("PASS")},
   371  						TestStatusEq{Status: shared.TestStatusValueFromString("OK")},
   372  					}},
   373  				}},
   374  			}}, rq)
   375  }
   376  
   377  func TestStructuredQuery_moreThan(t *testing.T) {
   378  	var rq RunQuery
   379  	err := json.Unmarshal([]byte(`{
   380  		"run_ids": [0, 1, 2],
   381  		"query": {
   382  			"exists": [{
   383  				"moreThan": 3,
   384  				"where": {"status":"PASS"}
   385  			}]
   386  		}
   387  	}`), &rq)
   388  	assert.Nil(t, err)
   389  	assert.Equal(
   390  		t,
   391  		RunQuery{RunIDs: []int64{0, 1, 2},
   392  			AbstractQuery: AbstractExists{[]AbstractQuery{
   393  				AbstractMoreThan{
   394  					AbstractCount{
   395  						Count: 3,
   396  						Where: TestStatusEq{Status: shared.TestStatusValueFromString("PASS")},
   397  					},
   398  				}},
   399  			}}, rq)
   400  }
   401  
   402  func TestStructuredQuery_lessThan(t *testing.T) {
   403  	var rq RunQuery
   404  	err := json.Unmarshal([]byte(`{
   405  		"run_ids": [0, 1, 2],
   406  		"query": {
   407  			"exists": [{
   408  				"lessThan": 2,
   409  				"where": {"status":"PASS"}
   410  			}]
   411  		}
   412  	}`), &rq)
   413  	assert.Nil(t, err)
   414  	assert.Equal(
   415  		t,
   416  		RunQuery{RunIDs: []int64{0, 1, 2},
   417  			AbstractQuery: AbstractExists{[]AbstractQuery{
   418  				AbstractLessThan{
   419  					AbstractCount{
   420  						Count: 2,
   421  						Where: TestStatusEq{Status: shared.TestStatusValueFromString("PASS")},
   422  					},
   423  				}},
   424  			}}, rq)
   425  }
   426  
   427  func TestStructuredQuery_link(t *testing.T) {
   428  	var rq RunQuery
   429  	err := json.Unmarshal([]byte(`{
   430  		"run_ids": [0, 1, 2],
   431  		"query": {
   432  			"exists": [{
   433  				"link": "chromium.bug.com/abc"
   434  			}]
   435  		}
   436  	}`), &rq)
   437  	assert.Nil(t, err)
   438  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   439  		AbstractQuery: AbstractExists{[]AbstractQuery{
   440  			AbstractLink{
   441  				Pattern: "chromium.bug.com/abc",
   442  			}},
   443  		}}, rq)
   444  }
   445  
   446  func TestStructuredQuery_triaged(t *testing.T) {
   447  	p := shared.ParseProductSpecUnsafe("Chrome")
   448  	var rq RunQuery
   449  	err := json.Unmarshal([]byte(`{
   450  		"run_ids": [0, 1, 2],
   451  		"query": {
   452  			"exists": [{
   453  				"triaged": "chrome"
   454  			}]
   455  		}
   456  	}`), &rq)
   457  	assert.Nil(t, err)
   458  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   459  		AbstractQuery: AbstractExists{[]AbstractQuery{
   460  			AbstractTriaged{
   461  				Product: &p,
   462  			}},
   463  		}}, rq)
   464  }
   465  
   466  func TestStructuredQuery_triagedEmptyProduct(t *testing.T) {
   467  	var rq RunQuery
   468  	err := json.Unmarshal([]byte(`{
   469  		"run_ids": [0, 1, 2],
   470  		"query": {
   471  			"exists": [{
   472  				"triaged": ""
   473  			}]
   474  		}
   475  	}`), &rq)
   476  	assert.Nil(t, err)
   477  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   478  		AbstractQuery: AbstractExists{[]AbstractQuery{
   479  			AbstractTriaged{
   480  				Product: nil,
   481  			}},
   482  		}}, rq)
   483  }
   484  
   485  func TestStructuredQuery_testlabel(t *testing.T) {
   486  	var rq RunQuery
   487  	err := json.Unmarshal([]byte(`{
   488          "run_ids": [0, 1, 2],
   489          "query": {
   490              "exists": [{
   491                  "label": "interop1"
   492              }]
   493          }
   494      }`), &rq)
   495  	assert.Nil(t, err)
   496  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   497  		AbstractQuery: AbstractExists{[]AbstractQuery{
   498  			AbstractTestLabel{
   499  				Label: "interop1",
   500  			}},
   501  		}}, rq)
   502  }
   503  
   504  func TestStructuredQuery_combinedTestlabel(t *testing.T) {
   505  	var rq RunQuery
   506  	err := json.Unmarshal([]byte(`{
   507          "run_ids": [0, 1, 2],
   508          "query": {
   509              "exists": [
   510                  {"pattern": "cssom"},
   511                  {"label": "interop"}
   512              ]
   513          }
   514      }`), &rq)
   515  	assert.Nil(t, err)
   516  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   517  		AbstractQuery: AbstractExists{[]AbstractQuery{TestNamePattern{"cssom"}, AbstractTestLabel{Label: "interop"}}}}, rq)
   518  }
   519  
   520  func TestStructuredQuery_andTestLabels(t *testing.T) {
   521  	var rq RunQuery
   522  	err := json.Unmarshal([]byte(`{
   523  		"run_ids": [0, 1, 2],
   524  		"query": {
   525  			"and": [
   526  				{"label": "interop1"},
   527  				{"label": "interop2"}
   528  			]
   529  		}
   530  	}`), &rq)
   531  	assert.Nil(t, err)
   532  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractAnd{[]AbstractQuery{AbstractTestLabel{Label: "interop1"}, AbstractTestLabel{Label: "interop2"}}}}, rq)
   533  }
   534  
   535  func TestStructuredQuery_testfeature(t *testing.T) {
   536  	var rq RunQuery
   537  	err := json.Unmarshal([]byte(`{
   538          "run_ids": [0, 1, 2],
   539          "query": {
   540              "exists": [{
   541                  "feature": "feature1"
   542              }]
   543          }
   544      }`), &rq)
   545  	assert.Nil(t, err)
   546  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   547  		AbstractQuery: AbstractExists{[]AbstractQuery{
   548  			AbstractTestWebFeature{
   549  				TestWebFeatureAtom: TestWebFeatureAtom{
   550  					WebFeature: "feature1",
   551  				},
   552  				manifestFetcher: searchcacheWebFeaturesManifestFetcher{},
   553  			}},
   554  		}}, rq)
   555  }
   556  
   557  func TestStructuredQuery_andTestFeatures(t *testing.T) {
   558  	var rq RunQuery
   559  	err := json.Unmarshal([]byte(`{
   560  		"run_ids": [0, 1, 2],
   561  		"query": {
   562  			"and": [
   563  				{"feature": "feature1"},
   564  				{"feature": "feature2"}
   565  			]
   566  		}
   567  	}`), &rq)
   568  	assert.Nil(t, err)
   569  	assert.Equal(t,
   570  		RunQuery{
   571  			RunIDs: []int64{0, 1, 2},
   572  			AbstractQuery: AbstractAnd{
   573  				[]AbstractQuery{
   574  					AbstractTestWebFeature{
   575  						TestWebFeatureAtom: TestWebFeatureAtom{
   576  							WebFeature: "feature1",
   577  						},
   578  						manifestFetcher: searchcacheWebFeaturesManifestFetcher{},
   579  					},
   580  					AbstractTestWebFeature{
   581  						TestWebFeatureAtom: TestWebFeatureAtom{
   582  							WebFeature: "feature2",
   583  						},
   584  						manifestFetcher: searchcacheWebFeaturesManifestFetcher{},
   585  					},
   586  				}}},
   587  		rq)
   588  }
   589  
   590  func TestStructuredQuery_isDifferent(t *testing.T) {
   591  	var rq RunQuery
   592  	err := json.Unmarshal([]byte(`{
   593  		"run_ids": [0, 1, 2],
   594  		"query": {
   595  			"exists": [{
   596  				"is": "different"
   597  			}]
   598  		}
   599  	}`), &rq)
   600  	assert.Nil(t, err)
   601  	assert.Equal(t, RunQuery{
   602  		RunIDs: []int64{0, 1, 2},
   603  		AbstractQuery: AbstractExists{[]AbstractQuery{
   604  			MetadataQualityDifferent,
   605  		}},
   606  	}, rq)
   607  }
   608  
   609  func TestStructuredQuery_isTentative(t *testing.T) {
   610  	var rq RunQuery
   611  	err := json.Unmarshal([]byte(`{
   612  		"run_ids": [0, 1, 2],
   613  		"query": {
   614  			"exists": [{
   615  				"is": "tentative"
   616  			}]
   617  		}
   618  	}`), &rq)
   619  	assert.Nil(t, err)
   620  	assert.Equal(t, RunQuery{
   621  		RunIDs: []int64{0, 1, 2},
   622  		AbstractQuery: AbstractExists{[]AbstractQuery{
   623  			MetadataQualityTentative,
   624  		}},
   625  	}, rq)
   626  }
   627  
   628  func TestStructuredQuery_isOptional(t *testing.T) {
   629  	var rq RunQuery
   630  	err := json.Unmarshal([]byte(`{
   631  		"run_ids": [0, 1, 2],
   632  		"query": {
   633  			"exists": [{
   634  				"is": "optional"
   635  			}]
   636  		}
   637  	}`), &rq)
   638  	assert.Nil(t, err)
   639  	assert.Equal(t, RunQuery{
   640  		RunIDs: []int64{0, 1, 2},
   641  		AbstractQuery: AbstractExists{[]AbstractQuery{
   642  			MetadataQualityOptional,
   643  		}},
   644  	}, rq)
   645  }
   646  
   647  func TestStructuredQuery_combinedlink(t *testing.T) {
   648  	var rq RunQuery
   649  	err := json.Unmarshal([]byte(`{
   650  		"run_ids": [0, 1, 2],
   651  		"query": {
   652  			"exists": [
   653  				{"pattern": "cssom"},
   654  				{"link": "chromium"}
   655  			]
   656  		}
   657  	}`), &rq)
   658  	assert.Nil(t, err)
   659  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   660  		AbstractQuery: AbstractExists{[]AbstractQuery{TestNamePattern{"cssom"}, AbstractLink{Pattern: "chromium"}}}}, rq)
   661  }
   662  
   663  func TestStructuredQuery_combinednotlink(t *testing.T) {
   664  	var rq RunQuery
   665  	err := json.Unmarshal([]byte(`{
   666  		"run_ids": [0, 1, 2],
   667  		"query": {
   668  			"exists": [
   669  				{"and": [
   670  					{"pattern": "cssom"},
   671  					{"not": {"link": "chromium.bug"}
   672  					}
   673  				  ]
   674  				}
   675  			]
   676  		}
   677  	}`), &rq)
   678  	assert.Nil(t, err)
   679  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   680  		AbstractQuery: AbstractExists{Args: []AbstractQuery{AbstractAnd{Args: []AbstractQuery{TestNamePattern{Pattern: "cssom"}, AbstractNot{Arg: AbstractLink{Pattern: "chromium.bug"}}}}}}}, rq)
   681  }
   682  
   683  func TestStructuredQuery_existsSimple(t *testing.T) {
   684  	var rq RunQuery
   685  	err := json.Unmarshal([]byte(`{
   686  		"run_ids": [0, 1, 2],
   687  		"query": {
   688  			"exists": [
   689  				{"and": [
   690  					{"pattern": "cssom"}
   691  				  ]
   692  				}
   693  			]
   694  		}
   695  	}`), &rq)
   696  	assert.Nil(t, err)
   697  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2},
   698  		AbstractQuery: AbstractExists{Args: []AbstractQuery{AbstractAnd{Args: []AbstractQuery{TestNamePattern{Pattern: "cssom"}}}}}}, rq)
   699  }
   700  
   701  func TestStructuredQuery_existsWithAnd(t *testing.T) {
   702  	var rq RunQuery
   703  	err := json.Unmarshal([]byte(`{
   704  		"run_ids": [0, 1, 2],
   705  		"query": {
   706  			"exists": [
   707  				{"pattern": "cssom"}
   708  			]
   709  		}
   710  	}`), &rq)
   711  	assert.Nil(t, err)
   712  	assert.Equal(t, RunQuery{RunIDs: []int64{0, 1, 2}, AbstractQuery: AbstractExists{[]AbstractQuery{TestNamePattern{"cssom"}}}}, rq)
   713  }
   714  
   715  func TestStructuredQuery_nested(t *testing.T) {
   716  	var rq RunQuery
   717  	err := json.Unmarshal([]byte(`{
   718  		"run_ids": [0, 1, 2],
   719  		"query": {
   720  			"or": [
   721  				{
   722  					"and": [
   723  						{"not": {"pattern": "cssom"}},
   724  						{"pattern": "html"}
   725  					]
   726  				},
   727  				{
   728  					"product": "eDgE",
   729  					"status": "tImEoUt"
   730  				}
   731  			]
   732  		}
   733  	}`), &rq)
   734  	assert.Nil(t, err)
   735  	p := shared.ParseProductSpecUnsafe("edge")
   736  	assert.Equal(t, RunQuery{
   737  		RunIDs: []int64{0, 1, 2},
   738  		AbstractQuery: AbstractOr{
   739  			Args: []AbstractQuery{
   740  				AbstractAnd{
   741  					Args: []AbstractQuery{
   742  						AbstractNot{TestNamePattern{"cssom"}},
   743  						TestNamePattern{"html"},
   744  					},
   745  				},
   746  				TestStatusEq{&p, shared.TestStatusValueFromString("TIMEOUT")},
   747  			},
   748  		},
   749  	}, rq)
   750  }
   751  
   752  func TestStructuredQuery_bindPattern(t *testing.T) {
   753  	tnp := TestNamePattern{
   754  		Pattern: "/",
   755  	}
   756  	q := tnp.BindToRuns()
   757  	assert.Equal(t, tnp, q)
   758  }
   759  
   760  func TestStructuredQuery_bindBrowserStatusNoRuns(t *testing.T) {
   761  	p := shared.ParseProductSpecUnsafe("Chrome")
   762  	assert.Equal(t, False{}, TestStatusEq{
   763  		Product: &p,
   764  		Status:  1,
   765  	}.BindToRuns())
   766  }
   767  
   768  func TestStructuredQuery_bindBrowserStatusSingleRun(t *testing.T) {
   769  	p := shared.ParseProductSpecUnsafe("firefox")
   770  	q := TestStatusEq{
   771  		Product: &p,
   772  		Status:  1,
   773  	}
   774  	runs := []shared.TestRun{
   775  		{
   776  			ID:                1,
   777  			ProductAtRevision: shared.ParseProductSpecUnsafe("Firefox").ProductAtRevision,
   778  		},
   779  		{
   780  			ID:                2,
   781  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
   782  		},
   783  	}
   784  	// Only Firefox run ID=1.
   785  	expected := RunTestStatusEq{
   786  		Run:    1,
   787  		Status: 1,
   788  	}
   789  	assert.Equal(t, expected, q.BindToRuns(runs...))
   790  }
   791  
   792  func TestStructuredQuery_bindBrowserStatusSingleRunNeq(t *testing.T) {
   793  	p := shared.ParseProductSpecUnsafe("firefox")
   794  	q := TestStatusNeq{
   795  		Product: &p,
   796  		Status:  1,
   797  	}
   798  	runs := []shared.TestRun{
   799  		{
   800  			ID:                1,
   801  			ProductAtRevision: shared.ParseProductSpecUnsafe("Firefox").ProductAtRevision,
   802  		},
   803  		{
   804  			ID:                2,
   805  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
   806  		},
   807  	}
   808  	// Only Firefox run ID=1.
   809  	expected := RunTestStatusNeq{
   810  		Run:    1,
   811  		Status: 1,
   812  	}
   813  	assert.Equal(t, expected, q.BindToRuns(runs...))
   814  }
   815  
   816  func TestStructuredQuery_bindStatusSomeRuns(t *testing.T) {
   817  	q := TestStatusNeq{
   818  		Status: 1,
   819  	}
   820  	runs := shared.TestRuns{
   821  		shared.TestRun{
   822  			ID:                1,
   823  			ProductAtRevision: shared.ParseProductSpecUnsafe("Firefox").ProductAtRevision,
   824  		},
   825  		shared.TestRun{
   826  			ID:                2,
   827  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
   828  		},
   829  	}
   830  	// Two Firefox runs: ID=1, ID=3.
   831  	expected := Or{
   832  		Args: []ConcreteQuery{
   833  			RunTestStatusNeq{
   834  				Run:    1,
   835  				Status: 1,
   836  			},
   837  			RunTestStatusNeq{
   838  				Run:    2,
   839  				Status: 1,
   840  			},
   841  		},
   842  	}
   843  	assert.Equal(t, expected, q.BindToRuns(runs...))
   844  }
   845  
   846  func TestStructuredQuery_bindBrowserStatusSomeRuns(t *testing.T) {
   847  	p := shared.ParseProductSpecUnsafe("firefox")
   848  	q := TestStatusNeq{
   849  		Product: &p,
   850  		Status:  1,
   851  	}
   852  	runs := []shared.TestRun{
   853  		{
   854  			ID:                1,
   855  			ProductAtRevision: shared.ParseProductSpecUnsafe("Firefox").ProductAtRevision,
   856  		},
   857  		{
   858  			ID:                2,
   859  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
   860  		},
   861  		{
   862  			ID:                3,
   863  			ProductAtRevision: shared.ParseProductSpecUnsafe("Firefox").ProductAtRevision,
   864  		},
   865  	}
   866  	// Two Firefox runs: ID=1, ID=3.
   867  	expected := Or{
   868  		Args: []ConcreteQuery{
   869  			RunTestStatusNeq{
   870  				Run:    1,
   871  				Status: 1,
   872  			},
   873  			RunTestStatusNeq{
   874  				Run:    3,
   875  				Status: 1,
   876  			},
   877  		},
   878  	}
   879  	assert.Equal(t, expected, q.BindToRuns(runs...))
   880  }
   881  
   882  func TestStructuredQuery_bindExists(t *testing.T) {
   883  	e := shared.ParseProductSpecUnsafe("edge")
   884  	f := shared.ParseProductSpecUnsafe("firefox")
   885  	q := AbstractExists{
   886  		Args: []AbstractQuery{
   887  			AbstractAnd{
   888  				Args: []AbstractQuery{
   889  					TestNamePattern{
   890  						Pattern: "/",
   891  					},
   892  					TestStatusEq{
   893  						Product: &e,
   894  						Status:  1,
   895  					},
   896  				},
   897  			},
   898  			TestStatusNeq{
   899  				Product: &f,
   900  				Status:  1,
   901  			},
   902  		},
   903  	}
   904  
   905  	runs := shared.TestRuns{}
   906  	or1 := Or{}
   907  	or2 := Or{}
   908  	products := []shared.ProductSpec{e, f}
   909  	for i := 1; i <= 10; i++ {
   910  		runs = append(
   911  			runs,
   912  			shared.TestRun{
   913  				ID:                int64(i),
   914  				ProductAtRevision: products[i%2].ProductAtRevision,
   915  			})
   916  		if i%2 == 0 { // Evens are edge
   917  			or1.Args = append(or1.Args,
   918  				And{
   919  					Args: []ConcreteQuery{
   920  						TestNamePattern{
   921  							Pattern: "/",
   922  						},
   923  						RunTestStatusEq{
   924  							Run:    int64(i),
   925  							Status: 1,
   926  						},
   927  					},
   928  				})
   929  		} else { // Odds are firefox
   930  			or2.Args = append(or2.Args,
   931  				RunTestStatusNeq{
   932  					Run:    int64(i),
   933  					Status: 1,
   934  				},
   935  			)
   936  		}
   937  	}
   938  	expected := And{
   939  		Args: []ConcreteQuery{or1, or2},
   940  	}
   941  	assert.Equal(t, expected, q.BindToRuns(runs...))
   942  }
   943  
   944  func TestStructuredQuery_bindExistsWithTwoProducts(t *testing.T) {
   945  	e := shared.ParseProductSpecUnsafe("edge")
   946  	f := shared.ParseProductSpecUnsafe("firefox")
   947  	q := AbstractExists{
   948  		Args: []AbstractQuery{
   949  			AbstractAnd{
   950  				Args: []AbstractQuery{
   951  					TestStatusEq{
   952  						Product: &e,
   953  						Status:  1,
   954  					},
   955  					TestStatusEq{
   956  						Product: &f,
   957  						Status:  1,
   958  					},
   959  				},
   960  			},
   961  		},
   962  	}
   963  
   964  	runs := shared.TestRuns{}
   965  	or1 := Or{}
   966  	or2 := Or{}
   967  	products := []shared.ProductSpec{e, f}
   968  	for i := 1; i <= 10; i++ {
   969  		runs = append(
   970  			runs,
   971  			shared.TestRun{
   972  				ID:                int64(i),
   973  				ProductAtRevision: products[i%2].ProductAtRevision,
   974  			})
   975  		if i%2 == 0 { // Evens are edge
   976  			or1.Args = append(or1.Args,
   977  				RunTestStatusEq{
   978  					Run:    int64(i),
   979  					Status: 1,
   980  				},
   981  			)
   982  		} else { // Odds are firefox
   983  			or2.Args = append(or2.Args,
   984  				RunTestStatusEq{
   985  					Run:    int64(i),
   986  					Status: 1,
   987  				},
   988  			)
   989  		}
   990  	}
   991  	expected := And{
   992  		Args: []ConcreteQuery{or1, or2},
   993  	}
   994  	assert.Equal(t, expected, q.BindToRuns(runs...))
   995  }
   996  
   997  func TestStructuredQuery_bindSequential(t *testing.T) {
   998  	e := shared.ParseProductSpecUnsafe("edge")
   999  	f := shared.ParseProductSpecUnsafe("firefox")
  1000  	q := AbstractSequential{
  1001  		Args: []AbstractQuery{
  1002  			TestStatusEq{Product: &e, Status: 1},
  1003  			TestStatusNeq{Product: &f, Status: 1},
  1004  		},
  1005  	}
  1006  
  1007  	runs := shared.TestRuns{}
  1008  	runs = shared.TestRuns{
  1009  		{
  1010  			ID:                int64(0),
  1011  			ProductAtRevision: e.ProductAtRevision,
  1012  		},
  1013  		{
  1014  			ID:                int64(1),
  1015  			ProductAtRevision: f.ProductAtRevision,
  1016  		},
  1017  	}
  1018  	seq := And{
  1019  		Args: []ConcreteQuery{
  1020  			RunTestStatusEq{Run: int64(0), Status: 1},
  1021  			RunTestStatusNeq{Run: int64(1), Status: 1},
  1022  		},
  1023  	}
  1024  	expected := Or{
  1025  		Args: []ConcreteQuery{seq},
  1026  	}
  1027  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1028  }
  1029  
  1030  func TestStructuredQuery_bindCount(t *testing.T) {
  1031  	e := shared.ParseProductSpecUnsafe("edge")
  1032  	f := shared.ParseProductSpecUnsafe("firefox")
  1033  	q := AbstractCount{
  1034  		Count: 1,
  1035  		Where: TestStatusEq{Status: 1},
  1036  	}
  1037  
  1038  	runs := shared.TestRuns{
  1039  		{
  1040  			ID:                int64(0),
  1041  			ProductAtRevision: e.ProductAtRevision,
  1042  		},
  1043  		{
  1044  			ID:                int64(1),
  1045  			ProductAtRevision: f.ProductAtRevision,
  1046  		},
  1047  	}
  1048  	expected := Count{
  1049  		Count: 1,
  1050  		Args: []ConcreteQuery{
  1051  			RunTestStatusEq{Run: int64(0), Status: 1},
  1052  			RunTestStatusEq{Run: int64(1), Status: 1},
  1053  		},
  1054  	}
  1055  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1056  }
  1057  
  1058  func TestStructuredQuery_bindLink(t *testing.T) {
  1059  	mockCtrl := gomock.NewController(t)
  1060  	defer mockCtrl.Finish()
  1061  
  1062  	sha := "sha"
  1063  	mockFetcher := sharedtest.NewMockMetadataFetcher(mockCtrl)
  1064  	mockFetcher.EXPECT().Fetch().Return(&sha, getMetadataTestData(), nil)
  1065  
  1066  	e := shared.ParseProductSpecUnsafe("safari")
  1067  	f := shared.ParseProductSpecUnsafe("firefox")
  1068  	q := AbstractLink{
  1069  		Pattern:         "bar",
  1070  		metadataFetcher: mockFetcher,
  1071  	}
  1072  
  1073  	runs := shared.TestRuns{
  1074  		{
  1075  			ID:                int64(0),
  1076  			ProductAtRevision: e.ProductAtRevision,
  1077  		},
  1078  		{
  1079  			ID:                int64(1),
  1080  			ProductAtRevision: f.ProductAtRevision,
  1081  		},
  1082  	}
  1083  
  1084  	// AbstractLink should bind test-level issues too as the pattern might match
  1085  	// them. It should not include the Chromium link however, as there is no run
  1086  	// for Chromium and thus no reason to include it - the frontend won't show it.
  1087  	expect := Link{
  1088  		Pattern: "bar",
  1089  		Metadata: map[string][]string{
  1090  			"/testB/b.html": {"bar.com"},
  1091  			"/testC/c.html": {"baz.com"},
  1092  		},
  1093  	}
  1094  	assert.Equal(t, expect, q.BindToRuns(runs...))
  1095  }
  1096  
  1097  func TestStructuredQuery_bindTriaged(t *testing.T) {
  1098  	mockCtrl := gomock.NewController(t)
  1099  	defer mockCtrl.Finish()
  1100  
  1101  	sha := "sha"
  1102  	mockFetcher := sharedtest.NewMockMetadataFetcher(mockCtrl)
  1103  	mockFetcher.EXPECT().Fetch().Return(&sha, getMetadataTestData(), nil).AnyTimes()
  1104  
  1105  	safari := shared.ParseProductSpecUnsafe("safari")
  1106  	firefox := shared.ParseProductSpecUnsafe("firefox")
  1107  	q := AbstractTriaged{
  1108  		Product:         &firefox,
  1109  		metadataFetcher: mockFetcher,
  1110  	}
  1111  
  1112  	runs := shared.TestRuns{
  1113  		{
  1114  			ID:                int64(0),
  1115  			ProductAtRevision: safari.ProductAtRevision,
  1116  		},
  1117  		{
  1118  			ID:                int64(1),
  1119  			ProductAtRevision: firefox.ProductAtRevision,
  1120  		},
  1121  	}
  1122  
  1123  	expect := Or{
  1124  		Args: []ConcreteQuery{
  1125  			Triaged{
  1126  				Run: 1,
  1127  				Metadata: map[string][]string{
  1128  					"/testB/b.html": {"bar.com"},
  1129  				},
  1130  			},
  1131  		},
  1132  	}
  1133  	assert.Equal(t, expect, q.BindToRuns(runs...))
  1134  
  1135  	// This query doesn't match any of the runs, so should convert to False.
  1136  	q = AbstractTriaged{
  1137  		Product:         &safari,
  1138  		metadataFetcher: mockFetcher,
  1139  	}
  1140  	assert.Equal(t, False{}, q.BindToRuns(runs...))
  1141  }
  1142  
  1143  func TestStructuredQuery_bindTriagedNilProduct(t *testing.T) {
  1144  	mockCtrl := gomock.NewController(t)
  1145  	defer mockCtrl.Finish()
  1146  
  1147  	sha := "sha"
  1148  	mockFetcher := sharedtest.NewMockMetadataFetcher(mockCtrl)
  1149  	mockFetcher.EXPECT().Fetch().Return(&sha, getMetadataTestData(), nil).AnyTimes()
  1150  
  1151  	q := AbstractTriaged{
  1152  		Product:         nil,
  1153  		metadataFetcher: mockFetcher,
  1154  	}
  1155  
  1156  	safari := shared.ParseProductSpecUnsafe("safari")
  1157  	firefox := shared.ParseProductSpecUnsafe("firefox")
  1158  	runs := shared.TestRuns{
  1159  		{
  1160  			ID:                int64(0),
  1161  			ProductAtRevision: safari.ProductAtRevision,
  1162  		},
  1163  		{
  1164  			ID:                int64(1),
  1165  			ProductAtRevision: firefox.ProductAtRevision,
  1166  		},
  1167  	}
  1168  
  1169  	// This is inefficient, but currently a nil product binds to all runs, with
  1170  	// the same metadata in all cases.
  1171  	expect := Or{
  1172  		Args: []ConcreteQuery{
  1173  			Triaged{
  1174  				Run: 0,
  1175  				Metadata: map[string][]string{
  1176  					"/testC/c.html": {"baz.com"},
  1177  				},
  1178  			},
  1179  			Triaged{
  1180  				Run: 1,
  1181  				Metadata: map[string][]string{
  1182  					"/testC/c.html": {"baz.com"},
  1183  				},
  1184  			},
  1185  		},
  1186  	}
  1187  	assert.Equal(t, expect, q.BindToRuns(runs...))
  1188  }
  1189  
  1190  func TestStructuredQuery_bindTestLabel(t *testing.T) {
  1191  	mockCtrl := gomock.NewController(t)
  1192  	defer mockCtrl.Finish()
  1193  
  1194  	sha := "sha"
  1195  	mockFetcher := sharedtest.NewMockMetadataFetcher(mockCtrl)
  1196  	mockFetcher.EXPECT().Fetch().Return(&sha, getMetadataTestData(), nil).AnyTimes()
  1197  
  1198  	safari := shared.ParseProductSpecUnsafe("safari")
  1199  	firefox := shared.ParseProductSpecUnsafe("firefox")
  1200  	q := AbstractTestLabel{
  1201  		Label:           "interop",
  1202  		metadataFetcher: mockFetcher,
  1203  	}
  1204  
  1205  	runs := shared.TestRuns{
  1206  		{
  1207  			ID:                int64(0),
  1208  			ProductAtRevision: safari.ProductAtRevision,
  1209  		},
  1210  		{
  1211  			ID:                int64(1),
  1212  			ProductAtRevision: firefox.ProductAtRevision,
  1213  		},
  1214  	}
  1215  
  1216  	expect := TestLabel{
  1217  		Label: "interop",
  1218  		Metadata: map[string][]string{
  1219  			"/testC/c.html": {"labelA"},
  1220  		},
  1221  	}
  1222  	assert.Equal(t, expect, q.BindToRuns(runs...))
  1223  }
  1224  
  1225  type testWebFeaturesManifestFetcher struct {
  1226  	data shared.WebFeaturesData
  1227  	err  error
  1228  }
  1229  
  1230  func (t testWebFeaturesManifestFetcher) Fetch() (shared.WebFeaturesData, error) {
  1231  	return t.data, t.err
  1232  }
  1233  
  1234  func TestStructuredQuery_bindTestWebFeature(t *testing.T) {
  1235  	mockManifestFetcher := testWebFeaturesManifestFetcher{
  1236  		data: shared.WebFeaturesData{
  1237  			"grid": {"/css/css-grid/bar.html": nil},
  1238  			"avif": {"/avif/foo.html": nil},
  1239  		},
  1240  		err: nil,
  1241  	}
  1242  
  1243  	safari := shared.ParseProductSpecUnsafe("safari")
  1244  	firefox := shared.ParseProductSpecUnsafe("firefox")
  1245  	q := AbstractTestWebFeature{
  1246  		TestWebFeatureAtom: TestWebFeatureAtom{
  1247  			WebFeature: "grid",
  1248  		},
  1249  		manifestFetcher: mockManifestFetcher,
  1250  	}
  1251  
  1252  	runs := shared.TestRuns{
  1253  		{
  1254  			ID:                int64(0),
  1255  			ProductAtRevision: safari.ProductAtRevision,
  1256  		},
  1257  		{
  1258  			ID:                int64(1),
  1259  			ProductAtRevision: firefox.ProductAtRevision,
  1260  		},
  1261  	}
  1262  
  1263  	expect := TestWebFeature{
  1264  		WebFeature: "grid",
  1265  		WebFeaturesData: shared.WebFeaturesData{
  1266  			"grid": {"/css/css-grid/bar.html": nil},
  1267  			"avif": {"/avif/foo.html": nil},
  1268  		},
  1269  	}
  1270  	assert.Equal(t, expect, q.BindToRuns(runs...))
  1271  }
  1272  
  1273  func TestStructuredQuery_bindIs(t *testing.T) {
  1274  	e := shared.ParseProductSpecUnsafe("chrome")
  1275  	f := shared.ParseProductSpecUnsafe("safari")
  1276  	q := MetadataQualityDifferent
  1277  
  1278  	runs := shared.TestRuns{
  1279  		{
  1280  			ID:                int64(0),
  1281  			ProductAtRevision: e.ProductAtRevision,
  1282  		},
  1283  		{
  1284  			ID:                int64(1),
  1285  			ProductAtRevision: f.ProductAtRevision,
  1286  		},
  1287  	}
  1288  	// BindToRuns for MetadataQuality is a no-op, as they are independent
  1289  	// of runs.
  1290  	assert.Equal(t, q, q.BindToRuns(runs...))
  1291  }
  1292  
  1293  func TestStructuredQuery_bindAnd(t *testing.T) {
  1294  	p := shared.ParseProductSpecUnsafe("edge")
  1295  	q := AbstractAnd{
  1296  		Args: []AbstractQuery{
  1297  			TestNamePattern{
  1298  				Pattern: "/",
  1299  			},
  1300  			TestStatusEq{
  1301  				Product: &p,
  1302  				Status:  1,
  1303  			},
  1304  		},
  1305  	}
  1306  	runs := []shared.TestRun{
  1307  		{
  1308  			ID:                1,
  1309  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1310  		},
  1311  	}
  1312  	// Only run is Edge, ID=1.
  1313  	expected := And{
  1314  		Args: []ConcreteQuery{
  1315  			TestNamePattern{
  1316  				Pattern: "/",
  1317  			},
  1318  			RunTestStatusEq{
  1319  				Run:    1,
  1320  				Status: 1,
  1321  			},
  1322  		},
  1323  	}
  1324  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1325  }
  1326  
  1327  func TestStructuredQuery_bindOr(t *testing.T) {
  1328  	p := shared.ParseProductSpecUnsafe("edge")
  1329  	q := AbstractOr{
  1330  		Args: []AbstractQuery{
  1331  			TestNamePattern{
  1332  				Pattern: "/",
  1333  			},
  1334  			TestStatusEq{
  1335  				Product: &p,
  1336  				Status:  1,
  1337  			},
  1338  		},
  1339  	}
  1340  	runs := []shared.TestRun{
  1341  		{
  1342  			ID:                1,
  1343  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1344  		},
  1345  	}
  1346  	// Only run is Edge, ID=1.
  1347  	expected := Or{
  1348  		Args: []ConcreteQuery{
  1349  			TestNamePattern{
  1350  				Pattern: "/",
  1351  			},
  1352  			RunTestStatusEq{
  1353  				Run:    1,
  1354  				Status: 1,
  1355  			},
  1356  		},
  1357  	}
  1358  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1359  }
  1360  
  1361  func TestStructuredQuery_bindNot(t *testing.T) {
  1362  	p := shared.ParseProductSpecUnsafe("edge")
  1363  	q := AbstractNot{
  1364  		Arg: TestStatusEq{
  1365  			Product: &p,
  1366  			Status:  1,
  1367  		},
  1368  	}
  1369  	runs := []shared.TestRun{
  1370  		{
  1371  			ID:                1,
  1372  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1373  		},
  1374  	}
  1375  	// Only run is Edge, ID=1.
  1376  	expected := Not{
  1377  		Arg: RunTestStatusEq{
  1378  			Run:    1,
  1379  			Status: 1,
  1380  		},
  1381  	}
  1382  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1383  }
  1384  
  1385  func TestStructuredQuery_bindAndReduce(t *testing.T) {
  1386  	p := shared.ParseProductSpecUnsafe("safari")
  1387  	q := AbstractAnd{
  1388  		Args: []AbstractQuery{
  1389  			TestNamePattern{
  1390  				Pattern: "/",
  1391  			},
  1392  			TestStatusEq{
  1393  				Product: &p,
  1394  				Status:  1,
  1395  			},
  1396  		},
  1397  	}
  1398  	runs := []shared.TestRun{
  1399  		{
  1400  			ID:                1,
  1401  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1402  		},
  1403  	}
  1404  	// No runs match Safari constraint; it becomes False,
  1405  	// False && Pattern="/" => False.
  1406  	assert.Equal(t, False{}, q.BindToRuns(runs...))
  1407  }
  1408  
  1409  func TestStructuredQuery_bindAndReduceToTrue(t *testing.T) {
  1410  	s := shared.ParseProductSpecUnsafe("safari")
  1411  	c := shared.ParseProductSpecUnsafe("chrome")
  1412  	q := AbstractAnd{
  1413  		Args: []AbstractQuery{
  1414  			TestStatusEq{
  1415  				Product: &c,
  1416  				Status:  1,
  1417  			},
  1418  			TestStatusNeq{
  1419  				Product: &s,
  1420  				Status:  1,
  1421  			},
  1422  		},
  1423  	}
  1424  	runs := []shared.TestRun{
  1425  		{
  1426  			ID:                1,
  1427  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1428  		},
  1429  	}
  1430  	// No runs match any constraint; reduce to False.
  1431  	assert.Equal(t, False{}, q.BindToRuns(runs...))
  1432  }
  1433  
  1434  func TestStructuredQuery_bindOrReduce(t *testing.T) {
  1435  	p := shared.ParseProductSpecUnsafe("safari")
  1436  	q := AbstractOr{
  1437  		Args: []AbstractQuery{
  1438  			TestNamePattern{
  1439  				Pattern: "/",
  1440  			},
  1441  			TestStatusEq{
  1442  				Product: &p,
  1443  				Status:  1,
  1444  			},
  1445  		},
  1446  	}
  1447  	runs := []shared.TestRun{
  1448  		{
  1449  			ID:                1,
  1450  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1451  		},
  1452  	}
  1453  	// No runs match Safari constraint; it becomes False,
  1454  	// Pattern="/" || False => Pattern.
  1455  	expected := TestNamePattern{"/"}
  1456  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1457  }
  1458  
  1459  func TestStructuredQuery_bindComplex(t *testing.T) {
  1460  	s := shared.ParseProductSpecUnsafe("safari")
  1461  	c := shared.ParseProductSpecUnsafe("chrome")
  1462  	q := AbstractOr{
  1463  		Args: []AbstractQuery{
  1464  			TestNamePattern{
  1465  				Pattern: "cssom",
  1466  			},
  1467  			AbstractAnd{
  1468  				Args: []AbstractQuery{
  1469  					AbstractNot{
  1470  						Arg: TestNamePattern{
  1471  							Pattern: "css",
  1472  						},
  1473  					},
  1474  					TestStatusEq{
  1475  						Product: &s,
  1476  						Status:  1,
  1477  					},
  1478  					TestStatusNeq{
  1479  						Product: &c,
  1480  						Status:  1,
  1481  					},
  1482  				},
  1483  			},
  1484  		},
  1485  	}
  1486  	runs := []shared.TestRun{
  1487  		{
  1488  			ID:                1,
  1489  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
  1490  		},
  1491  		{
  1492  			ID:                2,
  1493  			ProductAtRevision: shared.ParseProductSpecUnsafe("Edge").ProductAtRevision,
  1494  		},
  1495  		{
  1496  			ID:                3,
  1497  			ProductAtRevision: shared.ParseProductSpecUnsafe("Chrome").ProductAtRevision,
  1498  		},
  1499  	}
  1500  	// No runs match Safari constraint, so False; two Chrome runs expand to disjunction over
  1501  	// their values, but are combined with false in an AND, so False; leaving only
  1502  	// Pattern="cssom"
  1503  	expected := TestNamePattern{
  1504  		Pattern: "cssom",
  1505  	}
  1506  	assert.Equal(t, expected, q.BindToRuns(runs...))
  1507  }
  1508  
  1509  func getMetadataTestData() map[string][]byte {
  1510  	metadataMap := make(map[string][]byte)
  1511  	metadataMap["root/testA"] = []byte(`
  1512      links:
  1513        - product: chrome
  1514          url: foo.com
  1515          results:
  1516          - test: a.html
  1517            status: FAIL
  1518      `)
  1519  
  1520  	metadataMap["testB"] = []byte(`
  1521      links:
  1522        - product: firefox
  1523          url: bar.com
  1524          results:
  1525          - test: b.html
  1526            status: FAIL
  1527      `)
  1528  
  1529  	// A test-level issue, which has no product associated with it.
  1530  	metadataMap["testC"] = []byte(`
  1531      links:
  1532        - label: labelA
  1533          url: baz.com
  1534          results:
  1535          - test: c.html
  1536            status: FAIL
  1537      `)
  1538  
  1539  	return metadataMap
  1540  }