• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RauliL / juokse / 4520417188

pending completion
4520417188

push

github

GitHub
Merge pull request #12 from RauliL/more-test-cases

163 of 228 branches covered (71.49%)

Branch coverage included in aggregate %.

12 of 12 new or added lines in 2 files covered. (100.0%)

466 of 611 relevant lines covered (76.27%)

50.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.28
/src/execute.ts
1
import { flatten } from "lodash";
1✔
2

3
import {
1✔
4
  AssignmentStatement,
5
  BlockStatement,
6
  CommandStatement,
7
  ForStatement,
8
  IfStatement,
9
  Statement,
10
  StatementVisitor,
11
  visitStatement,
12
  WhileStatement,
13
} from "./ast";
14
import { Context, ExecutionResult } from "./context";
15
import {
1✔
16
  BreakError,
17
  CommandExitError,
18
  CommandKilledError,
19
  ContinueError,
20
} from "./error";
21
import { expandWord } from "./expand";
1✔
22
import { ExitStatus } from "./status";
1✔
23

24
export const visitor: StatementVisitor<Promise<ExecutionResult>, Context> = {
1✔
25
  async visitAssignment(
26
    statement: AssignmentStatement,
27
    context: Context
28
  ): Promise<ExecutionResult> {
29
    const value = (await expandWord(context, statement.value)).join(" ");
11✔
30

31
    context.variables[statement.variable] = value;
10✔
32

33
    return { status: ExitStatus.OK };
10✔
34
  },
35

36
  visitBlock(
37
    statement: BlockStatement,
38
    context: Context
39
  ): Promise<ExecutionResult> {
40
    return statement.body.reduce(
4✔
41
      (promise, child) =>
42
        promise.then(() => visitStatement(visitor, child, context)),
9✔
43
      Promise.resolve<ExecutionResult>({ status: ExitStatus.OK })
44
    );
45
  },
46

47
  async visitCommand(
48
    statement: CommandStatement,
49
    context: Context
50
  ): Promise<ExecutionResult> {
51
    const command = await expandWord(context, statement.command).then(
12✔
52
      (result) => result.join(" ")
12✔
53
    );
54
    const args = flatten(
12✔
55
      await Promise.all(statement.args.map((arg) => expandWord(context, arg)))
1✔
56
    );
57
    const result = await context.execute(command, ...args);
12✔
58

59
    if (result.signal || result.status !== ExitStatus.OK) {
10✔
60
      const id = [command, ...args].join(" ");
5✔
61

62
      if (result.signal) {
5!
63
        throw new CommandKilledError(
×
64
          `'${id}' was killed with signal ${result.signal}`
65
        );
66
      }
67

68
      throw new CommandExitError(`'${id}' exited with status ${result.status}`);
5✔
69
    }
70

71
    return result;
5✔
72
  },
73

74
  async visitFor(
75
    statement: ForStatement,
76
    context: Context
77
  ): Promise<ExecutionResult> {
78
    for (const subject of statement.subjects) {
2✔
79
      let canContinue = true;
4✔
80

81
      for (const value of await expandWord(context, subject)) {
4✔
82
        context.variables[statement.variable] = value;
4✔
83

84
        const loopStatus = await executeLoopBody(context, statement.body);
4✔
85

86
        if (loopStatus === "break") {
4✔
87
          canContinue = false;
1✔
88
          break;
1✔
89
        } else if (loopStatus === "continue") {
3!
90
          continue;
×
91
        }
92
      }
93

94
      if (!canContinue) {
4✔
95
        break;
1✔
96
      }
97
    }
98

99
    return { status: ExitStatus.OK };
2✔
100
  },
101

102
  async visitIf(
103
    statement: IfStatement,
104
    context: Context
105
  ): Promise<ExecutionResult> {
106
    const result = await evaluateCommand(context, statement.test);
3✔
107

108
    if (result.status === ExitStatus.OK) {
3✔
109
      return visitStatement(visitor, statement.then, context);
1✔
110
    } else if (statement.else) {
2✔
111
      return visitStatement(visitor, statement.else, context);
1✔
112
    }
113

114
    return { status: ExitStatus.OK };
1✔
115
  },
116

117
  async visitPass(): Promise<ExecutionResult> {
118
    return { status: ExitStatus.OK };
1✔
119
  },
120

121
  async visitWhile(
122
    statement: WhileStatement,
123
    context: Context
124
  ): Promise<ExecutionResult> {
125
    for (;;) {
2✔
126
      const { status } = await evaluateCommand(context, statement.test);
2✔
127

128
      if (status == ExitStatus.OK) {
2✔
129
        const loopStatus = await executeLoopBody(context, statement.body);
1✔
130

131
        if (loopStatus === "break") {
1!
132
          break;
1✔
133
        } else if (loopStatus === "continue") {
×
134
          continue;
×
135
        }
136
      } else {
137
        break;
1✔
138
      }
139
    }
140

141
    return { status: ExitStatus.OK };
2✔
142
  },
143
};
144

145
const evaluateCommand = (
1✔
146
  context: Context,
147
  command: CommandStatement
148
): Promise<ExecutionResult> =>
149
  visitor
5✔
150
    .visitCommand(command, context)
151
    .catch((error) =>
152
      error instanceof CommandExitError
3!
153
        ? { status: ExitStatus.ERROR }
154
        : Promise.reject(error)
155
    );
156

157
const executeLoopBody = async (
1✔
158
  context: Context,
159
  statement: Statement
160
): Promise<"break" | "continue" | undefined> => {
5✔
161
  try {
5✔
162
    await visitStatement(visitor, statement, context);
5✔
163
  } catch (err) {
164
    if (err instanceof BreakError) {
2!
165
      if (err.n > 1) {
2!
166
        throw new BreakError(err.n - 1);
×
167
      }
168

169
      return "break";
2✔
170
    } else if (err instanceof ContinueError) {
×
171
      if (err.n > 1) {
×
172
        throw new ContinueError(err.n - 1);
×
173
      }
174

175
      return "continue";
×
176
    }
177

178
    throw err;
×
179
  }
180
};
181

182
export const executeScript = (
1✔
183
  context: Context,
184
  script: Statement[],
185
  onError: (error: Error) => void
186
): void => {
187
  let index = 0;
×
188
  const executeNextStatement = (): void => {
×
189
    const statement = script[index++];
×
190

191
    if (statement) {
×
192
      visitStatement(visitor, statement, context)
×
193
        .catch(onError)
194
        .then(executeNextStatement);
195
    }
196
  };
197

198
  if (script.length > 0) {
×
199
    executeNextStatement();
×
200
  }
201
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc