Skip to content

Commit 132379f

Browse files
authored
Merge pull request #3489 from masatake/bats
Bats: Sh parser based new sub parser
2 parents 9072199 + 39c640e commit 132379f

File tree

13 files changed

+427
-15
lines changed

13 files changed

+427
-15
lines changed

Tmain/list-roles.d/stdout-expected.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Automake d/directory man on directory for MANS p
1717
Automake d/directory program on directory for PROGRAMS primary
1818
Automake d/directory script on directory for SCRIPTS primary
1919
Basic f/function decl on declared
20+
Bats S/script loaded on script loaed with "load" command
2021
C d/macro condition off used in part of #if/#ifdef/#elif conditions
2122
C d/macro undef on undefined
2223
C h/header local on local header
@@ -129,6 +130,7 @@ Automake d/directory man on directory for MANS p
129130
Automake d/directory program on directory for PROGRAMS primary
130131
Automake d/directory script on directory for SCRIPTS primary
131132
Basic f/function decl on declared
133+
Bats S/script loaded on script loaed with "load" command
132134
C d/macro condition off used in part of #if/#ifdef/#elif conditions
133135
C d/macro undef on undefined
134136
C h/header local on local header

Tmain/list-subparsers-all.d/stdout-expected.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ AnsiblePlaybook Yaml base <> sub {bidirectional}
33
Ant XML base <> sub {bidirectional}
44
Autoconf M4 base <> sub {bidirectional}
55
Automake Make base <= sub {dedicated}
6+
Bats Sh base <= sub {dedicated}
67
DBusIntrospect XML base <> sub {bidirectional}
78
FunctionParameters Perl base <> sub {bidirectional}
89
GemSpec Ruby base <= sub {dedicated}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--sort=no
2+
--extras=+sr
3+
--fields=+lr
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Zeroth test input.bats /^@test "Zeroth test" {$/;" t language:Bats roles:def
2+
First test input.bats /^@test "First test" {$/;" t language:Bats roles:def
3+
Second test input.bats /^@test "Second test" {$/;" t language:Bats roles:def
4+
Loading input.bats /^@test "Loading" {$/;" t language:Bats roles:def
5+
ABC input.bats /^ load "ABC"$/;" S language:Bats roles:loaded
6+
a"b input.bats /^ load "a\\"b"$/;" S language:Bats roles:loaded
7+
c\\d input.bats /^ load "c\\\\d"$/;" S language:Bats roles:loaded
8+
EFG input.bats /^ load EFG$/;" S language:Bats roles:loaded
9+
HIJ input.bats /^ load 'HIJ'$/;" S language:Bats roles:loaded
10+
h\\ij input.bats /^ load 'h\\ij'$/;" S language:Bats roles:loaded
11+
K L;M input.bats /^ load K\\ L\\;M$/;" S language:Bats roles:loaded
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bats
2+
3+
@test "Zeroth test" {
4+
# will have no tags
5+
}
6+
7+
# bats file_tags=a:b
8+
# bats test_tags=c:d
9+
10+
@test "First test" {
11+
# will be tagged a:b, c:d
12+
}
13+
14+
# bats file_tags=
15+
16+
@test "Second test" {
17+
# will have no tags
18+
}
19+
20+
@test "Loading" {
21+
load "ABC"
22+
load "a\"b"
23+
load "c\\d"
24+
load EFG
25+
load 'HIJ'
26+
load 'h\ij'
27+
load K\ L\;M
28+
}
29+
30+
# Taken from https://bats-core.readthedocs.io/en/stable/writing-tests.html

docs/news.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ The following parsers have been added:
407407
* D
408408
* DBusIntrospect *libxml*
409409
* Diff
410+
* Dots *Sh based subparser*
410411
* DTD
411412
* DTS
412413
* Elixir *optlib*

main/parsers_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
AutomakeParser, \
6868
AwkParser, \
6969
BasicParser, \
70+
BatsParser, \
7071
BetaParser, \
7172
BibtexParser, \
7273
ClojureParser, \

parsers/bats.c

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*
2+
* Copyright (c) 2022, Masatake YAMATO
3+
*
4+
* This source code is released for free distribution under the terms of the
5+
* GNU General Public License version 2 or (at your option) any later version.
6+
*
7+
* This module contains functions for generating tags for Bats scripts.
8+
*
9+
* Reference:
10+
* https://bats-core.readthedocs.io/en/stable/writing-tests.html
11+
*
12+
*/
13+
14+
#include "general.h" /* must always come first */
15+
16+
#include "debug.h"
17+
#include "entry.h"
18+
#include "kind.h"
19+
#include "parse.h"
20+
#include "sh.h"
21+
22+
#include <ctype.h>
23+
24+
25+
struct sBatsSubparser {
26+
shSubparser sh;
27+
int kind;
28+
int role;
29+
};
30+
31+
32+
typedef enum {
33+
R_SCRIPT_LOADED,
34+
} batsScriptRole;
35+
36+
static roleDefinition BatsScriptRoles [] = {
37+
{ true, "loaded", "script loaed with \"load\" command" },
38+
};
39+
40+
typedef enum {
41+
K_TEST,
42+
K_SCRIPT,
43+
} batsKind;
44+
45+
static kindDefinition BatsKinds[] = {
46+
{ true, 't', "test", "test cases", },
47+
{ true, 'S', "script", "scripts",
48+
.referenceOnly = true, ATTACH_ROLES(BatsScriptRoles)},
49+
};
50+
51+
52+
static void inputStart (subparser *s)
53+
{
54+
struct sBatsSubparser *bats = (struct sBatsSubparser*)s;
55+
56+
bats->kind = KIND_GHOST_INDEX;
57+
bats->role = ROLE_DEFINITION_INDEX;
58+
}
59+
60+
static int lineNotify (shSubparser *s, const unsigned char *cp)
61+
{
62+
struct sBatsSubparser *bats = (struct sBatsSubparser*)s;
63+
64+
if (cp[0] == '@' &&
65+
cp[1] == 't' &&
66+
cp[2] == 'e' &&
67+
cp[3] == 's' &&
68+
cp[4] == 't' &&
69+
isspace(cp[5]))
70+
{
71+
bats->kind = K_TEST;
72+
bats->role = ROLE_DEFINITION_INDEX;
73+
return 5;
74+
}
75+
else if (cp[0] == 'l' &&
76+
cp[1] == 'o' &&
77+
cp[2] == 'a' &&
78+
cp[3] == 'd' &&
79+
isspace(cp[4]))
80+
{
81+
bats->kind = K_SCRIPT;
82+
bats->role = R_SCRIPT_LOADED;
83+
return 4;
84+
}
85+
return 0;
86+
}
87+
88+
static int readDString (const unsigned char *cp, vString *name)
89+
{
90+
const unsigned char *initial = cp;
91+
bool escape = false;
92+
93+
while (*cp)
94+
{
95+
if (escape)
96+
{
97+
if (!(*cp == '\\' || *cp == '"'))
98+
vStringPut(name, '\\');
99+
vStringPut(name, *cp);
100+
escape = false;
101+
}
102+
else if (*cp == '\\')
103+
escape = true;
104+
else if (*cp == '"')
105+
break;
106+
else
107+
vStringPut(name, *cp);
108+
cp++;
109+
}
110+
111+
return cp - initial;
112+
}
113+
114+
static int readSString (const unsigned char *cp, vString *name)
115+
{
116+
const unsigned char *initial = cp;
117+
118+
while (*cp)
119+
{
120+
if (*cp == '\'')
121+
break;
122+
vStringPut(name, *cp);
123+
cp++;
124+
}
125+
126+
return cp - initial;
127+
}
128+
129+
static int readToken(const unsigned char *cp, vString *name)
130+
{
131+
const unsigned char *initial = cp;
132+
bool escape = false;
133+
134+
while (*cp)
135+
{
136+
if (escape)
137+
{
138+
vStringPut(name, *cp);
139+
escape = false;
140+
}
141+
else if (*cp == '\\')
142+
escape = true;
143+
else if (isspace(*cp)
144+
|| *cp == ';'
145+
|| *cp == '|'
146+
|| *cp == '&'
147+
|| *cp == '>'
148+
|| *cp == '<')
149+
break;
150+
else
151+
vStringPut(name, *cp);
152+
cp++;
153+
}
154+
155+
return cp - initial;
156+
}
157+
158+
static int extractName (shSubparser *s, const unsigned char *cp,
159+
vString *name)
160+
{
161+
int n;
162+
163+
if (*cp == '"')
164+
n = readDString(cp + 1, name);
165+
else if (*cp == '\'')
166+
n = readSString(cp + 1, name);
167+
else
168+
n = readToken(cp, name);
169+
170+
return n;
171+
}
172+
173+
static int makeTag (shSubparser *s, vString *name)
174+
{
175+
int r;
176+
struct sBatsSubparser *bats = (struct sBatsSubparser*)s;
177+
178+
Assert(bats->kind != KIND_GHOST_INDEX);
179+
r = makeSimpleRefTag(name, bats->kind, bats->role);
180+
bats->kind = KIND_GHOST_INDEX;
181+
bats->role = ROLE_DEFINITION_INDEX;
182+
183+
return r;
184+
}
185+
186+
static struct sBatsSubparser batsSubparser = {
187+
.sh = {
188+
.subparser = {
189+
.direction = SUBPARSER_SUB_RUNS_BASE,
190+
.inputStart = inputStart,
191+
},
192+
.lineNotify = lineNotify,
193+
.extractName = extractName,
194+
.makeTag = makeTag,
195+
},
196+
};
197+
198+
static void findBatsTags(void)
199+
{
200+
scheduleRunningBaseparser (0);
201+
}
202+
203+
extern parserDefinition* BatsParser (void)
204+
{
205+
static const char *const extensions [] = { "bats", NULL };
206+
parserDefinition* const def = parserNew("Bats");
207+
208+
static parserDependency dependencies [] = {
209+
[0] = { DEPTYPE_SUBPARSER, "Sh", &batsSubparser },
210+
};
211+
212+
def->dependencies = dependencies;
213+
def->dependencyCount = ARRAY_SIZE (dependencies);
214+
215+
def->kindTable = BatsKinds;
216+
def->kindCount = ARRAY_SIZE(BatsKinds);
217+
218+
def->extensions = extensions;
219+
def->parser = findBatsTags;
220+
def->useCork = CORK_QUEUE;
221+
222+
return def;
223+
}

0 commit comments

Comments
 (0)