1 // Copyright 2015 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "dyndep_parser.h"
16
17 #include <map>
18 #include <vector>
19
20 #include "dyndep.h"
21 #include "graph.h"
22 #include "state.h"
23 #include "test.h"
24
25 using namespace std;
26
27 struct DyndepParserTest : public testing::Test {
AssertParseDyndepParserTest28 void AssertParse(const char* input) {
29 DyndepParser parser(&state_, &fs_, &dyndep_file_);
30 string err;
31 EXPECT_TRUE(parser.ParseTest(input, &err));
32 ASSERT_EQ("", err);
33 }
34
SetUpDyndepParserTest35 virtual void SetUp() {
36 ::AssertParse(&state_,
37 "rule touch\n"
38 " command = touch $out\n"
39 "build out otherout: touch\n");
40 }
41
42 State state_;
43 VirtualFileSystem fs_;
44 DyndepFile dyndep_file_;
45 };
46
TEST_F(DyndepParserTest,Empty)47 TEST_F(DyndepParserTest, Empty) {
48 const char kInput[] =
49 "";
50 DyndepParser parser(&state_, &fs_, &dyndep_file_);
51 string err;
52 EXPECT_FALSE(parser.ParseTest(kInput, &err));
53 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n", err);
54 }
55
TEST_F(DyndepParserTest,Version1)56 TEST_F(DyndepParserTest, Version1) {
57 ASSERT_NO_FATAL_FAILURE(AssertParse(
58 "ninja_dyndep_version = 1\n"));
59 }
60
TEST_F(DyndepParserTest,Version1Extra)61 TEST_F(DyndepParserTest, Version1Extra) {
62 ASSERT_NO_FATAL_FAILURE(AssertParse(
63 "ninja_dyndep_version = 1-extra\n"));
64 }
65
TEST_F(DyndepParserTest,Version1_0)66 TEST_F(DyndepParserTest, Version1_0) {
67 ASSERT_NO_FATAL_FAILURE(AssertParse(
68 "ninja_dyndep_version = 1.0\n"));
69 }
70
TEST_F(DyndepParserTest,Version1_0Extra)71 TEST_F(DyndepParserTest, Version1_0Extra) {
72 ASSERT_NO_FATAL_FAILURE(AssertParse(
73 "ninja_dyndep_version = 1.0-extra\n"));
74 }
75
TEST_F(DyndepParserTest,CommentVersion)76 TEST_F(DyndepParserTest, CommentVersion) {
77 ASSERT_NO_FATAL_FAILURE(AssertParse(
78 "# comment\n"
79 "ninja_dyndep_version = 1\n"));
80 }
81
TEST_F(DyndepParserTest,BlankLineVersion)82 TEST_F(DyndepParserTest, BlankLineVersion) {
83 ASSERT_NO_FATAL_FAILURE(AssertParse(
84 "\n"
85 "ninja_dyndep_version = 1\n"));
86 }
87
TEST_F(DyndepParserTest,VersionCRLF)88 TEST_F(DyndepParserTest, VersionCRLF) {
89 ASSERT_NO_FATAL_FAILURE(AssertParse(
90 "ninja_dyndep_version = 1\r\n"));
91 }
92
TEST_F(DyndepParserTest,CommentVersionCRLF)93 TEST_F(DyndepParserTest, CommentVersionCRLF) {
94 ASSERT_NO_FATAL_FAILURE(AssertParse(
95 "# comment\r\n"
96 "ninja_dyndep_version = 1\r\n"));
97 }
98
TEST_F(DyndepParserTest,BlankLineVersionCRLF)99 TEST_F(DyndepParserTest, BlankLineVersionCRLF) {
100 ASSERT_NO_FATAL_FAILURE(AssertParse(
101 "\r\n"
102 "ninja_dyndep_version = 1\r\n"));
103 }
104
TEST_F(DyndepParserTest,VersionUnexpectedEOF)105 TEST_F(DyndepParserTest, VersionUnexpectedEOF) {
106 const char kInput[] =
107 "ninja_dyndep_version = 1.0";
108 DyndepParser parser(&state_, &fs_, &dyndep_file_);
109 string err;
110 EXPECT_FALSE(parser.ParseTest(kInput, &err));
111 EXPECT_EQ("input:1: unexpected EOF\n"
112 "ninja_dyndep_version = 1.0\n"
113 " ^ near here", err);
114 }
115
TEST_F(DyndepParserTest,UnsupportedVersion0)116 TEST_F(DyndepParserTest, UnsupportedVersion0) {
117 const char kInput[] =
118 "ninja_dyndep_version = 0\n";
119 DyndepParser parser(&state_, &fs_, &dyndep_file_);
120 string err;
121 EXPECT_FALSE(parser.ParseTest(kInput, &err));
122 EXPECT_EQ("input:1: unsupported 'ninja_dyndep_version = 0'\n"
123 "ninja_dyndep_version = 0\n"
124 " ^ near here", err);
125 }
126
TEST_F(DyndepParserTest,UnsupportedVersion1_1)127 TEST_F(DyndepParserTest, UnsupportedVersion1_1) {
128 const char kInput[] =
129 "ninja_dyndep_version = 1.1\n";
130 DyndepParser parser(&state_, &fs_, &dyndep_file_);
131 string err;
132 EXPECT_FALSE(parser.ParseTest(kInput, &err));
133 EXPECT_EQ("input:1: unsupported 'ninja_dyndep_version = 1.1'\n"
134 "ninja_dyndep_version = 1.1\n"
135 " ^ near here", err);
136 }
137
TEST_F(DyndepParserTest,DuplicateVersion)138 TEST_F(DyndepParserTest, DuplicateVersion) {
139 const char kInput[] =
140 "ninja_dyndep_version = 1\n"
141 "ninja_dyndep_version = 1\n";
142 DyndepParser parser(&state_, &fs_, &dyndep_file_);
143 string err;
144 EXPECT_FALSE(parser.ParseTest(kInput, &err));
145 EXPECT_EQ("input:2: unexpected identifier\n", err);
146 }
147
TEST_F(DyndepParserTest,MissingVersionOtherVar)148 TEST_F(DyndepParserTest, MissingVersionOtherVar) {
149 const char kInput[] =
150 "not_ninja_dyndep_version = 1\n";
151 DyndepParser parser(&state_, &fs_, &dyndep_file_);
152 string err;
153 EXPECT_FALSE(parser.ParseTest(kInput, &err));
154 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n"
155 "not_ninja_dyndep_version = 1\n"
156 " ^ near here", err);
157 }
158
TEST_F(DyndepParserTest,MissingVersionBuild)159 TEST_F(DyndepParserTest, MissingVersionBuild) {
160 const char kInput[] =
161 "build out: dyndep\n";
162 DyndepParser parser(&state_, &fs_, &dyndep_file_);
163 string err;
164 EXPECT_FALSE(parser.ParseTest(kInput, &err));
165 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n", err);
166 }
167
TEST_F(DyndepParserTest,UnexpectedEqual)168 TEST_F(DyndepParserTest, UnexpectedEqual) {
169 const char kInput[] =
170 "= 1\n";
171 DyndepParser parser(&state_, &fs_, &dyndep_file_);
172 string err;
173 EXPECT_FALSE(parser.ParseTest(kInput, &err));
174 EXPECT_EQ("input:1: unexpected '='\n", err);
175 }
176
TEST_F(DyndepParserTest,UnexpectedIndent)177 TEST_F(DyndepParserTest, UnexpectedIndent) {
178 const char kInput[] =
179 " = 1\n";
180 DyndepParser parser(&state_, &fs_, &dyndep_file_);
181 string err;
182 EXPECT_FALSE(parser.ParseTest(kInput, &err));
183 EXPECT_EQ("input:1: unexpected indent\n", err);
184 }
185
TEST_F(DyndepParserTest,OutDuplicate)186 TEST_F(DyndepParserTest, OutDuplicate) {
187 const char kInput[] =
188 "ninja_dyndep_version = 1\n"
189 "build out: dyndep\n"
190 "build out: dyndep\n";
191 DyndepParser parser(&state_, &fs_, &dyndep_file_);
192 string err;
193 EXPECT_FALSE(parser.ParseTest(kInput, &err));
194 EXPECT_EQ("input:3: multiple statements for 'out'\n"
195 "build out: dyndep\n"
196 " ^ near here", err);
197 }
198
TEST_F(DyndepParserTest,OutDuplicateThroughOther)199 TEST_F(DyndepParserTest, OutDuplicateThroughOther) {
200 const char kInput[] =
201 "ninja_dyndep_version = 1\n"
202 "build out: dyndep\n"
203 "build otherout: dyndep\n";
204 DyndepParser parser(&state_, &fs_, &dyndep_file_);
205 string err;
206 EXPECT_FALSE(parser.ParseTest(kInput, &err));
207 EXPECT_EQ("input:3: multiple statements for 'otherout'\n"
208 "build otherout: dyndep\n"
209 " ^ near here", err);
210 }
211
TEST_F(DyndepParserTest,NoOutEOF)212 TEST_F(DyndepParserTest, NoOutEOF) {
213 const char kInput[] =
214 "ninja_dyndep_version = 1\n"
215 "build";
216 DyndepParser parser(&state_, &fs_, &dyndep_file_);
217 string err;
218 EXPECT_FALSE(parser.ParseTest(kInput, &err));
219 EXPECT_EQ("input:2: unexpected EOF\n"
220 "build\n"
221 " ^ near here", err);
222 }
223
TEST_F(DyndepParserTest,NoOutColon)224 TEST_F(DyndepParserTest, NoOutColon) {
225 const char kInput[] =
226 "ninja_dyndep_version = 1\n"
227 "build :\n";
228 DyndepParser parser(&state_, &fs_, &dyndep_file_);
229 string err;
230 EXPECT_FALSE(parser.ParseTest(kInput, &err));
231 EXPECT_EQ("input:2: expected path\n"
232 "build :\n"
233 " ^ near here", err);
234 }
235
TEST_F(DyndepParserTest,OutNoStatement)236 TEST_F(DyndepParserTest, OutNoStatement) {
237 const char kInput[] =
238 "ninja_dyndep_version = 1\n"
239 "build missing: dyndep\n";
240 DyndepParser parser(&state_, &fs_, &dyndep_file_);
241 string err;
242 EXPECT_FALSE(parser.ParseTest(kInput, &err));
243 EXPECT_EQ("input:2: no build statement exists for 'missing'\n"
244 "build missing: dyndep\n"
245 " ^ near here", err);
246 }
247
TEST_F(DyndepParserTest,OutEOF)248 TEST_F(DyndepParserTest, OutEOF) {
249 const char kInput[] =
250 "ninja_dyndep_version = 1\n"
251 "build out";
252 DyndepParser parser(&state_, &fs_, &dyndep_file_);
253 string err;
254 EXPECT_FALSE(parser.ParseTest(kInput, &err));
255 EXPECT_EQ("input:2: unexpected EOF\n"
256 "build out\n"
257 " ^ near here", err);
258 }
259
TEST_F(DyndepParserTest,OutNoRule)260 TEST_F(DyndepParserTest, OutNoRule) {
261 const char kInput[] =
262 "ninja_dyndep_version = 1\n"
263 "build out:";
264 DyndepParser parser(&state_, &fs_, &dyndep_file_);
265 string err;
266 EXPECT_FALSE(parser.ParseTest(kInput, &err));
267 EXPECT_EQ("input:2: expected build command name 'dyndep'\n"
268 "build out:\n"
269 " ^ near here", err);
270 }
271
TEST_F(DyndepParserTest,OutBadRule)272 TEST_F(DyndepParserTest, OutBadRule) {
273 const char kInput[] =
274 "ninja_dyndep_version = 1\n"
275 "build out: touch";
276 DyndepParser parser(&state_, &fs_, &dyndep_file_);
277 string err;
278 EXPECT_FALSE(parser.ParseTest(kInput, &err));
279 EXPECT_EQ("input:2: expected build command name 'dyndep'\n"
280 "build out: touch\n"
281 " ^ near here", err);
282 }
283
TEST_F(DyndepParserTest,BuildEOF)284 TEST_F(DyndepParserTest, BuildEOF) {
285 const char kInput[] =
286 "ninja_dyndep_version = 1\n"
287 "build out: dyndep";
288 DyndepParser parser(&state_, &fs_, &dyndep_file_);
289 string err;
290 EXPECT_FALSE(parser.ParseTest(kInput, &err));
291 EXPECT_EQ("input:2: unexpected EOF\n"
292 "build out: dyndep\n"
293 " ^ near here", err);
294 }
295
TEST_F(DyndepParserTest,ExplicitOut)296 TEST_F(DyndepParserTest, ExplicitOut) {
297 const char kInput[] =
298 "ninja_dyndep_version = 1\n"
299 "build out exp: dyndep\n";
300 DyndepParser parser(&state_, &fs_, &dyndep_file_);
301 string err;
302 EXPECT_FALSE(parser.ParseTest(kInput, &err));
303 EXPECT_EQ("input:2: explicit outputs not supported\n"
304 "build out exp: dyndep\n"
305 " ^ near here", err);
306 }
307
TEST_F(DyndepParserTest,ExplicitIn)308 TEST_F(DyndepParserTest, ExplicitIn) {
309 const char kInput[] =
310 "ninja_dyndep_version = 1\n"
311 "build out: dyndep exp\n";
312 DyndepParser parser(&state_, &fs_, &dyndep_file_);
313 string err;
314 EXPECT_FALSE(parser.ParseTest(kInput, &err));
315 EXPECT_EQ("input:2: explicit inputs not supported\n"
316 "build out: dyndep exp\n"
317 " ^ near here", err);
318 }
319
TEST_F(DyndepParserTest,OrderOnlyIn)320 TEST_F(DyndepParserTest, OrderOnlyIn) {
321 const char kInput[] =
322 "ninja_dyndep_version = 1\n"
323 "build out: dyndep ||\n";
324 DyndepParser parser(&state_, &fs_, &dyndep_file_);
325 string err;
326 EXPECT_FALSE(parser.ParseTest(kInput, &err));
327 EXPECT_EQ("input:2: order-only inputs not supported\n"
328 "build out: dyndep ||\n"
329 " ^ near here", err);
330 }
331
TEST_F(DyndepParserTest,BadBinding)332 TEST_F(DyndepParserTest, BadBinding) {
333 const char kInput[] =
334 "ninja_dyndep_version = 1\n"
335 "build out: dyndep\n"
336 " not_restat = 1\n";
337 DyndepParser parser(&state_, &fs_, &dyndep_file_);
338 string err;
339 EXPECT_FALSE(parser.ParseTest(kInput, &err));
340 EXPECT_EQ("input:3: binding is not 'restat'\n"
341 " not_restat = 1\n"
342 " ^ near here", err);
343 }
344
TEST_F(DyndepParserTest,RestatTwice)345 TEST_F(DyndepParserTest, RestatTwice) {
346 const char kInput[] =
347 "ninja_dyndep_version = 1\n"
348 "build out: dyndep\n"
349 " restat = 1\n"
350 " restat = 1\n";
351 DyndepParser parser(&state_, &fs_, &dyndep_file_);
352 string err;
353 EXPECT_FALSE(parser.ParseTest(kInput, &err));
354 EXPECT_EQ("input:4: unexpected indent\n", err);
355 }
356
TEST_F(DyndepParserTest,NoImplicit)357 TEST_F(DyndepParserTest, NoImplicit) {
358 ASSERT_NO_FATAL_FAILURE(AssertParse(
359 "ninja_dyndep_version = 1\n"
360 "build out: dyndep\n"));
361
362 EXPECT_EQ(1u, dyndep_file_.size());
363 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
364 ASSERT_NE(i, dyndep_file_.end());
365 EXPECT_EQ(false, i->second.restat_);
366 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
367 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
368 }
369
TEST_F(DyndepParserTest,EmptyImplicit)370 TEST_F(DyndepParserTest, EmptyImplicit) {
371 ASSERT_NO_FATAL_FAILURE(AssertParse(
372 "ninja_dyndep_version = 1\n"
373 "build out | : dyndep |\n"));
374
375 EXPECT_EQ(1u, dyndep_file_.size());
376 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
377 ASSERT_NE(i, dyndep_file_.end());
378 EXPECT_EQ(false, i->second.restat_);
379 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
380 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
381 }
382
TEST_F(DyndepParserTest,ImplicitIn)383 TEST_F(DyndepParserTest, ImplicitIn) {
384 ASSERT_NO_FATAL_FAILURE(AssertParse(
385 "ninja_dyndep_version = 1\n"
386 "build out: dyndep | impin\n"));
387
388 EXPECT_EQ(1u, dyndep_file_.size());
389 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
390 ASSERT_NE(i, dyndep_file_.end());
391 EXPECT_EQ(false, i->second.restat_);
392 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
393 ASSERT_EQ(1u, i->second.implicit_inputs_.size());
394 EXPECT_EQ("impin", i->second.implicit_inputs_[0]->path());
395 }
396
TEST_F(DyndepParserTest,ImplicitIns)397 TEST_F(DyndepParserTest, ImplicitIns) {
398 ASSERT_NO_FATAL_FAILURE(AssertParse(
399 "ninja_dyndep_version = 1\n"
400 "build out: dyndep | impin1 impin2\n"));
401
402 EXPECT_EQ(1u, dyndep_file_.size());
403 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
404 ASSERT_NE(i, dyndep_file_.end());
405 EXPECT_EQ(false, i->second.restat_);
406 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
407 ASSERT_EQ(2u, i->second.implicit_inputs_.size());
408 EXPECT_EQ("impin1", i->second.implicit_inputs_[0]->path());
409 EXPECT_EQ("impin2", i->second.implicit_inputs_[1]->path());
410 }
411
TEST_F(DyndepParserTest,ImplicitOut)412 TEST_F(DyndepParserTest, ImplicitOut) {
413 ASSERT_NO_FATAL_FAILURE(AssertParse(
414 "ninja_dyndep_version = 1\n"
415 "build out | impout: dyndep\n"));
416
417 EXPECT_EQ(1u, dyndep_file_.size());
418 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
419 ASSERT_NE(i, dyndep_file_.end());
420 EXPECT_EQ(false, i->second.restat_);
421 ASSERT_EQ(1u, i->second.implicit_outputs_.size());
422 EXPECT_EQ("impout", i->second.implicit_outputs_[0]->path());
423 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
424 }
425
TEST_F(DyndepParserTest,ImplicitOuts)426 TEST_F(DyndepParserTest, ImplicitOuts) {
427 ASSERT_NO_FATAL_FAILURE(AssertParse(
428 "ninja_dyndep_version = 1\n"
429 "build out | impout1 impout2 : dyndep\n"));
430
431 EXPECT_EQ(1u, dyndep_file_.size());
432 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
433 ASSERT_NE(i, dyndep_file_.end());
434 EXPECT_EQ(false, i->second.restat_);
435 ASSERT_EQ(2u, i->second.implicit_outputs_.size());
436 EXPECT_EQ("impout1", i->second.implicit_outputs_[0]->path());
437 EXPECT_EQ("impout2", i->second.implicit_outputs_[1]->path());
438 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
439 }
440
TEST_F(DyndepParserTest,ImplicitInsAndOuts)441 TEST_F(DyndepParserTest, ImplicitInsAndOuts) {
442 ASSERT_NO_FATAL_FAILURE(AssertParse(
443 "ninja_dyndep_version = 1\n"
444 "build out | impout1 impout2: dyndep | impin1 impin2\n"));
445
446 EXPECT_EQ(1u, dyndep_file_.size());
447 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
448 ASSERT_NE(i, dyndep_file_.end());
449 EXPECT_EQ(false, i->second.restat_);
450 ASSERT_EQ(2u, i->second.implicit_outputs_.size());
451 EXPECT_EQ("impout1", i->second.implicit_outputs_[0]->path());
452 EXPECT_EQ("impout2", i->second.implicit_outputs_[1]->path());
453 ASSERT_EQ(2u, i->second.implicit_inputs_.size());
454 EXPECT_EQ("impin1", i->second.implicit_inputs_[0]->path());
455 EXPECT_EQ("impin2", i->second.implicit_inputs_[1]->path());
456 }
457
TEST_F(DyndepParserTest,Restat)458 TEST_F(DyndepParserTest, Restat) {
459 ASSERT_NO_FATAL_FAILURE(AssertParse(
460 "ninja_dyndep_version = 1\n"
461 "build out: dyndep\n"
462 " restat = 1\n"));
463
464 EXPECT_EQ(1u, dyndep_file_.size());
465 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
466 ASSERT_NE(i, dyndep_file_.end());
467 EXPECT_EQ(true, i->second.restat_);
468 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
469 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
470 }
471
TEST_F(DyndepParserTest,OtherOutput)472 TEST_F(DyndepParserTest, OtherOutput) {
473 ASSERT_NO_FATAL_FAILURE(AssertParse(
474 "ninja_dyndep_version = 1\n"
475 "build otherout: dyndep\n"));
476
477 EXPECT_EQ(1u, dyndep_file_.size());
478 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
479 ASSERT_NE(i, dyndep_file_.end());
480 EXPECT_EQ(false, i->second.restat_);
481 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
482 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
483 }
484
TEST_F(DyndepParserTest,MultipleEdges)485 TEST_F(DyndepParserTest, MultipleEdges) {
486 ::AssertParse(&state_,
487 "build out2: touch\n");
488 ASSERT_EQ(2u, state_.edges_.size());
489 ASSERT_EQ(1u, state_.edges_[1]->outputs_.size());
490 EXPECT_EQ("out2", state_.edges_[1]->outputs_[0]->path());
491 EXPECT_EQ(0u, state_.edges_[0]->inputs_.size());
492
493 ASSERT_NO_FATAL_FAILURE(AssertParse(
494 "ninja_dyndep_version = 1\n"
495 "build out: dyndep\n"
496 "build out2: dyndep\n"
497 " restat = 1\n"));
498
499 EXPECT_EQ(2u, dyndep_file_.size());
500 {
501 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
502 ASSERT_NE(i, dyndep_file_.end());
503 EXPECT_EQ(false, i->second.restat_);
504 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
505 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
506 }
507 {
508 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[1]);
509 ASSERT_NE(i, dyndep_file_.end());
510 EXPECT_EQ(true, i->second.restat_);
511 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
512 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
513 }
514 }
515