-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtestscenarios.html
230 lines (210 loc) · 14.5 KB
/
testscenarios.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<style>
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ body { font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; font-size: 14px; padding: 0 12px; line-height: 22px; word-wrap: break-word; } body.scrollBeyondLastLine { margin-bottom: calc(100vh - 22px); } body.showEditorSelection .code-line { position: relative; } body.showEditorSelection .code-active-line:before, body.showEditorSelection .code-line:hover:before { content: ""; display: block; position: absolute; top: 0; left: -12px; height: 100%; } body.showEditorSelection li.code-active-line:before, body.showEditorSelection li.code-line:hover:before { left: -30px; } .vscode-light.showEditorSelection .code-active-line:before { border-left: 3px solid rgba(0, 0, 0, 0.15); } .vscode-light.showEditorSelection .code-line:hover:before { border-left: 3px solid rgba(0, 0, 0, 0.40); } .vscode-dark.showEditorSelection .code-active-line:before { border-left: 3px solid rgba(255, 255, 255, 0.4); } .vscode-dark.showEditorSelection .code-line:hover:before { border-left: 3px solid rgba(255, 255, 255, 0.60); } .vscode-high-contrast.showEditorSelection .code-active-line:before { border-left: 3px solid rgba(255, 160, 0, 0.7); } .vscode-high-contrast.showEditorSelection .code-line:hover:before { border-left: 3px solid rgba(255, 160, 0, 1); } img { max-width: 100%; max-height: 100%; } a { color: #4080D0; text-decoration: none; } a:focus, input:focus, select:focus, textarea:focus { outline: 1px solid -webkit-focus-ring-color; outline-offset: -1px; } hr { border: 0; height: 2px; border-bottom: 2px solid; } h1 { padding-bottom: 0.3em; line-height: 1.2; border-bottom-width: 1px; border-bottom-style: solid; } h1, h2, h3 { font-weight: normal; } h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { font-size: inherit; line-height: auto; } a:hover { color: #4080D0; text-decoration: underline; } table { border-collapse: collapse; } table > thead > tr > th { text-align: left; border-bottom: 1px solid; } table > thead > tr > th, table > thead > tr > td, table > tbody > tr > th, table > tbody > tr > td { padding: 5px 10px; } table > tbody > tr + tr > td { border-top: 1px solid; } blockquote { margin: 0 7px 0 5px; padding: 0 16px 0 10px; border-left: 5px solid; } code { font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; font-size: 14px; line-height: 19px; } body.wordWrap pre { white-space: pre-wrap; } .mac code { font-size: 12px; line-height: 18px; } code > div { padding: 16px; border-radius: 3px; overflow: auto; } /** Theming */ .vscode-light { color: rgb(30, 30, 30); } .vscode-dark { color: #DDD; } .vscode-high-contrast { color: white; } .vscode-light code { color: #A31515; } .vscode-dark code { color: #D7BA7D; } .vscode-light code > div { background-color: rgba(220, 220, 220, 0.4); } .vscode-dark code > div { background-color: rgba(10, 10, 10, 0.4); } .vscode-high-contrast code > div { background-color: rgb(0, 0, 0); } .vscode-high-contrast h1 { border-color: rgb(0, 0, 0); } .vscode-light table > thead > tr > th { border-color: rgba(0, 0, 0, 0.69); } .vscode-dark table > thead > tr > th { border-color: rgba(255, 255, 255, 0.69); } .vscode-light h1, .vscode-light hr, .vscode-light table > tbody > tr + tr > td { border-color: rgba(0, 0, 0, 0.18); } .vscode-dark h1, .vscode-dark hr, .vscode-dark table > tbody > tr + tr > td { border-color: rgba(255, 255, 255, 0.18); } .vscode-light blockquote, .vscode-dark blockquote { background: rgba(127, 127, 127, 0.1); border-color: rgba(0, 122, 204, 0.5); } .vscode-high-contrast blockquote { background: transparent; border-color: #fff; }
</style>
<style>
/* Tomorrow Theme */ /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ /* Original theme - https://github.com/chriskempson/tomorrow-theme */ /* Tomorrow Comment */ .hljs-comment, .hljs-quote { color: #8e908c; } /* Tomorrow Red */ .hljs-variable, .hljs-template-variable, .hljs-tag, .hljs-name, .hljs-selector-id, .hljs-selector-class, .hljs-regexp, .hljs-deletion { color: #c82829; } /* Tomorrow Orange */ .hljs-number, .hljs-built_in, .hljs-builtin-name, .hljs-literal, .hljs-type, .hljs-params, .hljs-meta, .hljs-link { color: #f5871f; } /* Tomorrow Yellow */ .hljs-attribute { color: #eab700; } /* Tomorrow Green */ .hljs-string, .hljs-symbol, .hljs-bullet, .hljs-addition { color: #718c00; } /* Tomorrow Blue */ .hljs-title, .hljs-section { color: #4271ae; } /* Tomorrow Purple */ .hljs-keyword, .hljs-selector-tag { color: #8959a8; } .hljs { display: block; overflow-x: auto; color: #4d4d4c; padding: 0.5em; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; }
</style>
<style>
ul.contains-task-list { padding-left: 0; } ul ul.contains-task-list { padding-left: 40px; } .task-list-item { list-style-type: none; } .task-list-item-checkbox { vertical-align: middle; }
</style>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif;
font-size: 14px;
line-height: 1.6;
}
</style>
</head>
<body>
<h1 id="test-scenario-structure">Test scenario structure</h1>
<p>Test scenarios are described in JSON files with the following structure:</p>
<pre><code class="language-{"> "name": "...(free) name of the test scenario..."
"options": {
"grammar": "...relative path+file name of the .ne file",
"lexer": "...relative path+file of the lexer .js file",
"methods": // array of method testing
[
{
"method": "...(free) name of the testing method...".
"interpreter": "none" | "...relative path+file of rule interpreter .js, see below..."
"field": "name of the 'expected' field value, see below..."
"file": "...relative path+file to the output file...; .txt for text, .html for html output"
"start": "...name of the start rule..."
},
// ... multiple methods
]
},
"cases": // array of individual test cases
[
{ // a single test case
"name": "...(free) name of a test case...",
"input": "...input text to parse...",
"...expexted field name 1...": [...expected result performing the associated method..., see below...]
...
"...expexted field name n...": "...expected result performing the associated method..., see below..."
}
]
}
</code></pre>
<h1 id="interpreter-files">Interpreter files</h1>
<p>3 default interpreters are provided</p>
<ol>
<li>"none": default interpreter to collect entire, valid sequences that correspond to the grammar</li>
<li>"unit-interpreter.js": those rules, marked with "unit" preprocessing function are considered as parenthesized</li>
<li>"unitvalue-interpreter.js" rules marked with value computation are parenthesized and values are computed and checked</li>
</ol>
<p>The content of the expected value varies when using the different interpreters.</p>
<h1 id="field-of-methods">"Field" of methods</h1>
<p>The field name is linked to the "expected field" of "cases", i.e. for a particular method the expected value given under the same field will be compared againts the received value.</p>
<h1 id="content-of-the-expected-value">Content of the expected value</h1>
<h2 id="default-interpreter-none">Default interpreter ("none")</h2>
<p>The value is an array of strings, whereas each string is a valid sentence according to the grammar.
Non-matching parts are not presented.</p>
<p>Example:</p>
<p>considering the grammar collecting sequences of ingredients of a recipe separated by commas</p>
<pre><code>"methods": [{
"interpreter":"none",
"field":"sequence",
...
}]
...
cases:[{
"input":"Recipe: 1 kg flour, 2 l milk, mix well; add 1 g salt, 1 l vinegar, mix again",
"sequence":["1 kg flour, 2 l milk","1 g salt, 1 l vinegar"],
...]}
</code></pre>
<h2 id="unit-interpreter">Unit interpreter</h2>
<p>The value is an array of strings about the same as in the "sequence", in which "units" marked in the grammar are parenthesized.</p>
<p>Example .ne:</p>
<pre><code>ingredient -> %measure %space %name {%"unit"%}
</code></pre>
<p>Example test .json:</p>
<pre><code> "interpreter":"../lib/unit-interpreter.js",
"field":"unit",
...
cases:[{
"input":"Recipe: 1 kg flour, 2 l milk, mix well; add 1 g salt, 1 l vinegar, mix again",
"unit":["{1 kg flour}, {2 l milk}","{1 g salt}, {1 l vinegar}"],
...
</code></pre>
<h2 id="unit-value-interpreter">Unit value interpreter</h2>
<p>The computed, generated values of each unit are added within each unit.</p>
<p>The values are considered as name-value pairs, either given in the rule as constant or computed from the values of the matched atoms of the matching rule.</p>
<p>Example .ne:</p>
<pre><code>ingredient -> %measure %space %name
{%{ "function": "unit","unitType":"ingredient", "fields":{"measure":0,"name":2} }%}
</code></pre>
<p>The called function here is the "unit" function, with some options.
The syntax of the function call is as follows:</p>
<pre><code> { "function": "...name of the function to call...",
// this is "unit" now, because we use both the 'unit' and 'unit value' interpreter
// however, this can be replaced with other functions (in harmony to the interpreter)
"...constant field 1...": "...constant field value 1..." // some constant fields
// unit type is given in the above example
"fields":{
"...fieldName 1 ...":"...integer 1, the index of matched element in the rule...",
...
}
</code></pre>
<p>In the above example:
"measure":0 accesses the %measure, that is the 0th terminal in the rule
"name": 2 accesses the %name, that is the 2nd terminal in the rule</p>
<p>However, non-terminals can be also used there (to be tested)</p>
<p>Please note that for atoms, the substituted values of the input values are collected.
This substutition is described in the lexer file.</p>
<p>As a test case:</p>
<pre><code>methods:[{
"interpreter":"../lib/unitvalue-interpreter.js",
"field":"unitvalue",
...
}]
...
"cases": [{
"input":"Recipe: 1 kg flour, 2 l milk, mix well; add 1 g salt, 1 l vinegar, mix again",
"unitvalue":["{1 kg flour}={measure=1'kg,name=flour,unitType=ingredient}, {2 l milk}={measure=2'l,name=milk},unitType=ingredient","{1 g salt}={measure=1'g,name=salt,unitType=ingredient}, {1 l vinegar}={measure=1'l,name=vinegar,unitType=ingredient}"]
}]
</code></pre>
<p>The test case contains for each matched unit in {}, the collected values in format</p>
<pre><code>{...found unit 1...}={...unit values...}<fillers in the rule>{...found unit 2...}={...unit values...}
</code></pre>
<p>where as unit values is a comma-separated list of values computed from the rule or from the static parameters as described above.</p>
<h1 id="complete-example">Complete example</h1>
<h2 id="lexer">Lexer</h2>
<pre><code>let {atom,regexp,Lexer,use} = require('../lib/lexer-tolerant.js');
let myRecipeLexer = new Lexer("myRecipeLexer");
use(myRecipeLexer);
atom("measure",{"volume":"","weight":"","number":""}).substitute().beforeSeparator().afterSeparator();
atom("number",regexp("[1-9][0-9]*")).substitute();
atom("weightnumber",["number"]);
atom("weight",["weightnumber","space","weightunit"]).substitute((x)=>(x.weightnumber+"'"+x.weightunit)).beforeSeparator().afterSeparator()
atom("weightunit",regexp("(g|kg|oz)")).substitute().standalone().beforeSeparator()
atom("volumenumber",["number"]);
atom("volume",["volumenumber","space","volumeunit"]).substitute((x)=>(x.volumenumber+"'"+x.volumeunit)).beforeSeparator().afterSeparator()
atom("volumeunit",regexp("(l|ml)")).substitute().standalone().beforeSeparator()
atom("name",regexp("[a-z_]+")).substitute().beforeSeparator().afterSeparator();
atom("white",regexp("[ \\t\\n]+"));
atom("space",regexp("[ ]"));
atom("comma",regexp(",[ \\t\\n]+"));
atom("action",regexp("[A-Z]+"))
myRecipeLexer.setStart("measure");
module.exports = myRecipeLexer;
</code></pre>
<h2 id="parser">Parser</h2>
<pre><code>@lexer measureLexer
start -> ingredient moreIngredients actions {%"repeat"%}
actions -> action:*
moreIngredients -> (%comma ingredient):*
ingredient -> %measure %space %name {%{ "function": "unit",
"unitType":"ingredient", "fields":{"measure":0,"name":2} }%}
action -> %action
</code></pre>
<h2 id="test-file-to-be-revised">Test file (to be revised!)</h2>
<pre><code>{
"name":"Measure test cases",
"options": {
"grammar":"measure.ne",
"lexer":"measure-lexer.js",
"methods":
[
{
"method":"findText",
"interpreter":"none",
"field":"sequence",
"file": "measure.sequence.found.txt"
},
{
"method":"findUnit",
"interpreter":"../lib/unit-interpreter.js",
"field":"unit",
"file": "measure.unit.found.txt"
},
{
"method":"findUnitValue",
"interpreter":"../lib/unitvalue-interpreter.js",
"field":"unitvalue",
"file": "measure.value.found.txt"
}
]
},
"cases":[
{ "name":"1/1",
"input":"Recipe: 1 kg flour, 2 l milk, mix well; add 1 g salt, 1 l vinegar, mix again",
"sequence":["1 kg flour, 2 l milk","1 g salt, 1 l vinegar"],
"unit":["{1 kg flour}, {2 l milk}","{1 g salt}, {1 l vinegar}"],
"unitvalue":["{1 kg flour}={measure=1'kg,name=flour}, {2 l milk}={measure=2'l,name=milk}","{1 g salt}={measure=1'g,name=salt}, {1 l vinegar}={measure=1'l,name=vinegar}"]
},
{ "name":"1/2",
"input":"Recipe: 1 g salt, 1 l vinegar and something else",
"sequence":["1 g salt, 1 l vinegar"],
"unit":["{1 g salt}, {1 l vinegar}"],
"unitvalue":["{1 g salt}={measure=1'g,name=salt}, {1 l vinegar}={measure=1'l,name=vinegar}"]
}
]
}
</code></pre>
</body>
</html>