| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import chai from 'chai'; // not esm |
| import {LitElement} from 'lit'; |
| import { LogRow } from '../components/log_row'; |
| import { FilterExpression, OrExpression, parseFilter } from '../src/filter'; |
| |
| before(() => { |
| chai.should(); |
| }); |
| |
| describe('filtering', () => { |
| let testLitElements: LitElement[] = []; |
| let testElements: HTMLElement[] = []; |
| |
| before(async () => { |
| testLitElements = [ |
| new LogRow( |
| { |
| data: { |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| TargetLog: { |
| moniker: 'core/hello-world', |
| metadata: { |
| errors: [], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| component_url: 'fuchsia-pkg://fuchsia.com/hello#meta/logs.cm', |
| timestamp: 123456789, |
| severity: 'Info', |
| tags: ['fofo', 'bar',], |
| pid: 123, |
| tid: 456, |
| file: 'foo/main.rs', |
| line: 25, |
| }, |
| payload: { |
| root: { |
| message: { |
| value: 'hello vscode' |
| }, |
| keys: { |
| 'string': 'oh no' |
| }, |
| printf: undefined |
| }, |
| }, |
| }, |
| }, |
| timestamp: 12345, |
| version: 1 |
| }, false), |
| new LogRow( //2 |
| { |
| data: { |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| TargetLog: { |
| moniker: 'core/bye-world', |
| metadata: { |
| errors: [], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| component_url: 'fuchsia-pkg://fuchsia.com/bye#meta/logging.cm', |
| timestamp: 123456789, |
| severity: 'error', |
| tags: ['foo'], |
| pid: 789, |
| tid: 987, |
| file: 'foo/main.rs', |
| line: 25, |
| }, |
| payload: { |
| root: { |
| message: { |
| value: 'goodbye vscode' |
| }, |
| keys: { |
| 'num': 5, |
| 'bool': false, |
| 'string': 'hey hey', |
| }, |
| printf: undefined |
| }, |
| }, |
| }, |
| }, |
| timestamp: 12345, |
| version: 1 |
| }, false), |
| new LogRow( |
| { |
| data: { |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| TargetLog: { |
| moniker: 'bootstrap/hello-world', |
| metadata: { |
| errors: [], |
| // eslint-disable-next-line @typescript-eslint/naming-convention |
| component_url: 'fuchsia-pkg://fuchsia.com/hello#meta/logs.cm', |
| timestamp: 123456789, |
| severity: 'warn', |
| tags: ['baz'], |
| pid: 123, |
| tid: 789, |
| file: 'foo/main.rs', |
| line: 25, |
| }, |
| payload: { |
| root: { |
| message: { |
| value: 'hello goodbye vscode' |
| }, |
| keys: { |
| 'num': 10, |
| 'bool': true, |
| 'string': 'hey there', |
| }, |
| printf: undefined |
| }, |
| }, |
| }, |
| }, |
| timestamp: 12345, |
| version: 1 |
| }, false), |
| ]; |
| |
| for (const index in testLitElements) { |
| document.body.appendChild(testLitElements[index]); |
| await testLitElements[index].updateComplete; |
| testElements.push(testLitElements[index].children[0] as HTMLElement); |
| } |
| }); |
| |
| after(() => { |
| for (const index in testLitElements) { |
| testLitElements[index].remove(); |
| } |
| }); |
| |
| describe('FilterExpression', () => { |
| describe('accepts', () => { |
| it('returns true when no filters are set', () => { |
| const expr = new OrExpression([]); |
| expr.accepts(testElements[0]).should.be.true; |
| expr.accepts(testElements[1]).should.be.true; |
| }); |
| |
| it('ANDs the given filters', () => { |
| const result = parseFilter('moniker:hello-world severity:warn'); |
| result.ok.should.be.true; |
| if (result.ok) { |
| result.value.accepts(testElements[0]).should.be.false; |
| result.value.accepts(testElements[1]).should.be.false; |
| result.value.accepts(testElements[2]).should.be.true; |
| } |
| }); |
| |
| it('ORs multiple categories', () => { |
| const result = parseFilter('(moniker:bootstrap | message="hello vscode")'); |
| result.ok.should.be.true; |
| if (result.ok) { |
| result.value.accepts(testElements[0]).should.be.true; |
| result.value.accepts(testElements[1]).should.be.false; |
| result.value.accepts(testElements[2]).should.be.true; |
| } |
| }); |
| |
| it('applies not', () => { |
| const result = parseFilter('!moniker:bootstrap'); |
| result.ok.should.be.true; |
| if (result.ok) { |
| result.value.accepts(testElements[0]).should.be.true; |
| result.value.accepts(testElements[1]).should.be.true; |
| result.value.accepts(testElements[2]).should.be.false; |
| } |
| }); |
| }); |
| }); |
| |
| describe('Filter', () => { |
| /** |
| * Utility that parses a filter string and returns the first filter parsed. |
| * |
| * @param expr a string with a filter |
| * @returns the `Filter` parsed. |
| */ |
| function singleFilter(expr: string): FilterExpression { |
| const result = parseFilter(expr); |
| if (result.ok) { |
| return result.value; |
| } |
| throw Error(`failed to parse ${expr}: ${result.error}`); |
| } |
| |
| describe('accepts', () => { |
| describe('ORs the potential values of filters', () => { |
| it('supports ORing multiple values for single category', () => { |
| const filter = singleFilter('severity=warn|error'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| }); |
| |
| describe('severity', () => { |
| it('handles minimum severity', () => { |
| let filter = singleFilter('severity:error'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('severity>=warn'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('severity>warn'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('handles exact severity', () => { |
| let filter = singleFilter('severity=warn'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('handles maximum severity', () => { |
| let filter = singleFilter('severity<warn'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('severity<=warn'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| }); |
| |
| describe('moniker', () => { |
| it('handles contains', () => { |
| let filter = singleFilter('moniker:hello'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('handles equals', () => { |
| let filter = singleFilter('moniker=core/bye-world'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('handles regexes', () => { |
| let filter = singleFilter('moniker=/^core.+$/'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('moniker:/he.*lo/'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| }); |
| |
| describe('pid, tid', () => { |
| it('handle contains', () => { |
| let filter = singleFilter('pid:123'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('tid:456'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('handle equals', () => { |
| let filter = singleFilter('pid=123'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('tid=456'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| }); |
| |
| describe('tag', () => { |
| it('handles contains', () => { |
| let filter = singleFilter('tag:fo'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('handles equals', () => { |
| let filter = singleFilter('tag=foo'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| }); |
| |
| describe('package-name', () => { |
| it('handles contains', () => { |
| let filter = singleFilter('package-name:lo'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('handles equals', () => { |
| let filter = singleFilter('package-name=bye'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| }); |
| |
| describe('manifest', () => { |
| it('handles contains', () => { |
| let filter = singleFilter('manifest:log'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('handles equals', () => { |
| let filter = singleFilter('manifest=logs.cm'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| }); |
| |
| describe('message', () => { |
| it('handles contains', () => { |
| let filter = singleFilter('message:goodbye'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('handles equals', () => { |
| let filter = singleFilter('message="goodbye vscode"'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| }); |
| |
| describe('custom', () => { |
| it('supports string ops on string fields', () => { |
| let filter = singleFilter('string:hey'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('string="hey hey"'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('supports equals and contains with regexes', () => { |
| let filter = singleFilter('string:/^hey/'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('string=/o.*no/'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('supports numeric ops on number fields', () => { |
| let filter = singleFilter('num>3'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('num>=10'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('num=5'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('num:10'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('num<10'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('num<=10'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('supports equals and contains (as equals) on boolean fields', () => { |
| let filter = singleFilter('bool=true'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('bool:false'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| }); |
| |
| describe('any', () => { |
| it('supports string ops on any field', () => { |
| let filter = singleFilter('any=hello'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('any:hey'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| |
| it('supports raw strings', () => { |
| let filter = singleFilter('hello'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| |
| filter = singleFilter('not hello'); |
| filter.accepts(testElements[0]).should.be.false; |
| filter.accepts(testElements[1]).should.be.true; |
| filter.accepts(testElements[2]).should.be.false; |
| |
| filter = singleFilter('"hello vscode"'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.false; |
| }); |
| |
| it('supports raw regexes', () => { |
| let filter = singleFilter('/hel+o/'); |
| filter.accepts(testElements[0]).should.be.true; |
| filter.accepts(testElements[1]).should.be.false; |
| filter.accepts(testElements[2]).should.be.true; |
| }); |
| }); |
| }); |
| }); |
| }); |