1 /*
2 * Copyright (C) 2015, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "options.h"
18
19 #include <iostream>
20 #include <memory>
21 #include <string>
22 #include <vector>
23
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26
27 #include "diagnostics.h"
28
29 using android::aidl::DiagnosticID;
30 using android::aidl::DiagnosticSeverity;
31 using std::cerr;
32 using std::endl;
33 using std::string;
34 using std::unique_ptr;
35 using std::vector;
36 using testing::internal::CaptureStderr;
37 using testing::internal::GetCapturedStderr;
38
39 namespace android {
40 namespace aidl {
41 namespace {
42
43 const char kPreprocessCommandOutputFile[] = "output_file_name";
44 const char kPreprocessCommandInput1[] = "input1.aidl";
45 const char kPreprocessCommandInput2[] = "input2.aidl";
46 const char kPreprocessCommandInput3[] = "input3.aidl";
47 const char* kPreprocessCommand[] = {
48 "aidl", "--preprocess",
49 kPreprocessCommandOutputFile,
50 kPreprocessCommandInput1,
51 kPreprocessCommandInput2,
52 kPreprocessCommandInput3,
53 nullptr,
54 };
55
56 const char kCompileCommandInput[] = "directory/ITool.aidl";
57 const char kCompileCommandIncludePath[] = "-Iinclude_path";
58 const char* kCompileJavaCommand[] = {
59 "aidl",
60 "-b",
61 kCompileCommandIncludePath,
62 kCompileCommandInput,
63 nullptr,
64 };
65 const char kCompileCommandJavaOutput[] = "directory/ITool.java";
66
67 const char kCompileDepFileNinja[] = "--ninja";
68 const char* kCompileJavaCommandNinja[] = {
69 "aidl",
70 "-b",
71 kCompileDepFileNinja,
72 kCompileCommandIncludePath,
73 kCompileCommandInput,
74 nullptr,
75 };
76
77 const char kCompileDepFile[] = "-doutput.deps";
78 const char kCompileCommandHeaderDir[] = "output/dir/";
79 const char kCompileCommandCppOutput[] = "some/file.cpp";
80 const char* kCompileCppCommand[] = {
81 "aidl-cpp",
82 kCompileCommandIncludePath,
83 kCompileDepFile,
84 kCompileCommandInput,
85 kCompileCommandHeaderDir,
86 kCompileCommandCppOutput,
87 nullptr,
88 };
89 const char* kCompileCppCommandNinja[] = {
90 "aidl-cpp",
91 kCompileCommandIncludePath,
92 kCompileDepFile,
93 kCompileDepFileNinja,
94 kCompileCommandInput,
95 kCompileCommandHeaderDir,
96 kCompileCommandCppOutput,
97 nullptr,
98 };
99
GetOptions(const char * command[],Options::Language default_lang=Options::Language::JAVA)100 unique_ptr<Options> GetOptions(const char* command[],
101 Options::Language default_lang = Options::Language::JAVA) {
102 int argc = 0;
103 const char** command_part = command;
104 for (; *command_part; ++argc, ++command_part) {}
105 unique_ptr<Options> ret(new Options(argc, command, default_lang));
106 if (!ret->Ok()) {
107 cerr << ret->GetErrorMessage();
108 cerr << "Failed to parse command line:";
109 for (int i = 0; i < argc; ++i) {
110 cerr << " " << command[i];
111 cerr << endl;
112 }
113 }
114 EXPECT_NE(ret, nullptr) << "Failed to parse options!";
115 return ret;
116 }
117
118 } // namespace
119
TEST(OptionsTests,ParsesPreprocess)120 TEST(OptionsTests, ParsesPreprocess) {
121 unique_ptr<Options> options = GetOptions(kPreprocessCommand);
122 EXPECT_EQ(Options::Task::PREPROCESS, options->GetTask());
123 EXPECT_EQ(false, options->FailOnParcelable());
124 EXPECT_EQ(0u, options->ImportDirs().size());
125 EXPECT_EQ(0u, options->PreprocessedFiles().size());
126 EXPECT_EQ(string{kPreprocessCommandOutputFile}, options->OutputFile());
127 EXPECT_EQ(false, options->AutoDepFile());
128 const vector<string> expected_input{kPreprocessCommandInput1,
129 kPreprocessCommandInput2,
130 kPreprocessCommandInput3};
131 EXPECT_EQ(expected_input, options->InputFiles());
132 }
133
TEST(OptionsTests,ParsesCompileJava)134 TEST(OptionsTests, ParsesCompileJava) {
135 unique_ptr<Options> options = GetOptions(kCompileJavaCommand);
136 EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
137 EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
138 EXPECT_EQ(true, options->FailOnParcelable());
139 EXPECT_EQ(1u, options->ImportDirs().size());
140 EXPECT_EQ(0u, options->PreprocessedFiles().size());
141 EXPECT_EQ(string{kCompileCommandInput}, options->InputFiles().front());
142 EXPECT_EQ(string{kCompileCommandJavaOutput}, options->OutputFile());
143 EXPECT_EQ(false, options->AutoDepFile());
144 EXPECT_EQ(false, options->DependencyFileNinja());
145 }
146
TEST(OptionsTests,ParsesCompileJavaNinja)147 TEST(OptionsTests, ParsesCompileJavaNinja) {
148 unique_ptr<Options> options = GetOptions(kCompileJavaCommandNinja);
149 EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
150 EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
151 EXPECT_EQ(true, options->FailOnParcelable());
152 EXPECT_EQ(1u, options->ImportDirs().size());
153 EXPECT_EQ(0u, options->PreprocessedFiles().size());
154 EXPECT_EQ(string{kCompileCommandInput}, options->InputFiles().front());
155 EXPECT_EQ(string{kCompileCommandJavaOutput}, options->OutputFile());
156 EXPECT_EQ(false, options->AutoDepFile());
157 EXPECT_EQ(true, options->DependencyFileNinja());
158 }
159
TEST(OptionsTests,ParsesCompileCpp)160 TEST(OptionsTests, ParsesCompileCpp) {
161 unique_ptr<Options> options = GetOptions(kCompileCppCommand, Options::Language::CPP);
162 ASSERT_EQ(1u, options->ImportDirs().size());
163 EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), *options->ImportDirs().begin());
164 EXPECT_EQ(string{kCompileDepFile}.substr(2), options->DependencyFile());
165 EXPECT_EQ(false, options->DependencyFileNinja());
166 EXPECT_EQ(kCompileCommandInput, options->InputFiles().front());
167 EXPECT_EQ(kCompileCommandHeaderDir, options->OutputHeaderDir());
168 EXPECT_EQ(kCompileCommandCppOutput, options->OutputFile());
169 }
170
TEST(OptionsTests,ParsesCompileCppNinja)171 TEST(OptionsTests, ParsesCompileCppNinja) {
172 unique_ptr<Options> options = GetOptions(kCompileCppCommandNinja, Options::Language::CPP);
173 ASSERT_EQ(1u, options->ImportDirs().size());
174 EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2), *options->ImportDirs().begin());
175 EXPECT_EQ(string{kCompileDepFile}.substr(2), options->DependencyFile());
176 EXPECT_EQ(true, options->DependencyFileNinja());
177 EXPECT_EQ(kCompileCommandInput, options->InputFiles().front());
178 EXPECT_EQ(kCompileCommandHeaderDir, options->OutputHeaderDir());
179 EXPECT_EQ(kCompileCommandCppOutput, options->OutputFile());
180 }
181
TEST(OptionsTests,ParsesCompileJavaMultiInput)182 TEST(OptionsTests, ParsesCompileJavaMultiInput) {
183 const char* argv[] = {
184 "aidl",
185 "--lang=java",
186 kCompileCommandIncludePath,
187 "-o src_out",
188 "directory/input1.aidl",
189 "directory/input2.aidl",
190 "directory/input3.aidl",
191 nullptr,
192 };
193 unique_ptr<Options> options = GetOptions(argv);
194 EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
195 EXPECT_EQ(Options::Language::JAVA, options->TargetLanguage());
196 EXPECT_EQ(false, options->FailOnParcelable());
197 EXPECT_EQ(1u, options->ImportDirs().size());
198 EXPECT_EQ(0u, options->PreprocessedFiles().size());
199 const vector<string> expected_input{"directory/input1.aidl", "directory/input2.aidl",
200 "directory/input3.aidl"};
201 EXPECT_EQ(expected_input, options->InputFiles());
202 EXPECT_EQ(string{""}, options->OutputFile());
203 EXPECT_EQ(false, options->AutoDepFile());
204 EXPECT_EQ(false, options->DependencyFileNinja());
205 EXPECT_EQ(string{""}, options->OutputHeaderDir());
206 EXPECT_EQ(string{"src_out/"}, options->OutputDir());
207 }
208
TEST(OptionsTests,ParsesCompileRust)209 TEST(OptionsTests, ParsesCompileRust) {
210 const char* argv[] = {
211 "aidl", "--lang=rust", kCompileCommandIncludePath,
212 "-o src_out", kCompileCommandInput, nullptr,
213 };
214 unique_ptr<Options> options = GetOptions(argv);
215 EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
216 EXPECT_EQ(Options::Language::RUST, options->TargetLanguage());
217 EXPECT_EQ(false, options->FailOnParcelable());
218 EXPECT_EQ(1u, options->ImportDirs().size());
219 EXPECT_EQ(0u, options->PreprocessedFiles().size());
220 EXPECT_EQ(string{kCompileCommandInput}, options->InputFiles().front());
221 EXPECT_EQ(string{""}, options->OutputFile());
222 EXPECT_EQ(string{""}, options->OutputHeaderDir());
223 EXPECT_EQ(string{"src_out/"}, options->OutputDir());
224 EXPECT_EQ(false, options->AutoDepFile());
225 EXPECT_EQ(false, options->DependencyFileNinja());
226 }
227
TEST(OptionsTests,ParsesCompileJavaInvalid_OutRequired)228 TEST(OptionsTests, ParsesCompileJavaInvalid_OutRequired) {
229 // -o option is required
230 string expected_error = "Output directory is not set. Set with --out.";
231 CaptureStderr();
232 const char* arg_with_no_out_dir[] = {
233 "aidl",
234 "--lang=java",
235 kCompileCommandIncludePath,
236 "directory/input1.aidl",
237 "directory/input2.aidl",
238 "directory/input3.aidl",
239 nullptr,
240 };
241 EXPECT_EQ(false, GetOptions(arg_with_no_out_dir)->Ok());
242 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
243 }
244
TEST(OptionsTests,ParsesCompileJavaInvalid_RejectHeaderOut)245 TEST(OptionsTests, ParsesCompileJavaInvalid_RejectHeaderOut) {
246 string expected_error = "Header output directory is set, which does not make sense for Java.";
247 CaptureStderr();
248 // -h options is not for Java
249 const char* arg_with_header_dir[] = {
250 "aidl", "--lang=java", kCompileCommandIncludePath, "-o src_out",
251 "-h header_out", "directory/input1.aidl", "directory/input2.aidl", "directory/input3.aidl",
252 nullptr,
253 };
254 EXPECT_EQ(false, GetOptions(arg_with_header_dir)->Ok());
255 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
256 }
257
TEST(OptionsTests,ParsesCompileCppMultiInput)258 TEST(OptionsTests, ParsesCompileCppMultiInput) {
259 const char* argv[] = {
260 "aidl",
261 "--lang=cpp",
262 kCompileCommandIncludePath,
263 "-h header_out",
264 "-o src_out",
265 "directory/input1.aidl",
266 "directory/input2.aidl",
267 "directory/input3.aidl",
268 nullptr,
269 };
270 unique_ptr<Options> options = GetOptions(argv);
271 EXPECT_EQ(Options::Task::COMPILE, options->GetTask());
272 EXPECT_EQ(Options::Language::CPP, options->TargetLanguage());
273 EXPECT_EQ(false, options->FailOnParcelable());
274 EXPECT_EQ(1u, options->ImportDirs().size());
275 EXPECT_EQ(0u, options->PreprocessedFiles().size());
276 const vector<string> expected_input{"directory/input1.aidl", "directory/input2.aidl",
277 "directory/input3.aidl"};
278 EXPECT_EQ(expected_input, options->InputFiles());
279 EXPECT_EQ(string{""}, options->OutputFile());
280 EXPECT_EQ(false, options->AutoDepFile());
281 EXPECT_EQ(false, options->DependencyFileNinja());
282 EXPECT_EQ(string{"header_out/"}, options->OutputHeaderDir());
283 EXPECT_EQ(string{"src_out/"}, options->OutputDir());
284 }
285
TEST(OptionsTests,ParsesCompileCppInvalid_OutRequired)286 TEST(OptionsTests, ParsesCompileCppInvalid_OutRequired) {
287 // -o option is required
288 string expected_error = "Output directory is not set. Set with --out.";
289 CaptureStderr();
290 const char* arg_with_no_out_dir[] = {
291 "aidl",
292 "--lang=cpp",
293 kCompileCommandIncludePath,
294 "directory/input1.aidl",
295 "directory/input2.aidl",
296 "directory/input3.aidl",
297 nullptr,
298 };
299 EXPECT_EQ(false, GetOptions(arg_with_no_out_dir)->Ok());
300 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
301 }
302
TEST(OptionsTests,ParsesCompileCppInvalid_HeaderOutRequired)303 TEST(OptionsTests, ParsesCompileCppInvalid_HeaderOutRequired) {
304 // -h options is required as well
305 string expected_error = "Header output directory is not set. Set with --header_out";
306 CaptureStderr();
307 const char* arg_with_no_header_dir[] = {
308 "aidl",
309 "--lang=cpp",
310 kCompileCommandIncludePath,
311 "-o src_out",
312 "directory/input1.aidl",
313 "directory/input2.aidl",
314 "directory/input3.aidl",
315 nullptr,
316 };
317 EXPECT_EQ(false, GetOptions(arg_with_no_header_dir)->Ok());
318 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
319 }
320
TEST(OptionsTests,ParsesCompileRustInvalid_OutRequired)321 TEST(OptionsTests, ParsesCompileRustInvalid_OutRequired) {
322 // -o option is required
323 string expected_error = "Output directory is not set. Set with --out";
324 CaptureStderr();
325 const char* arg_with_no_out_dir[] = {
326 "aidl",
327 "--lang=rust",
328 kCompileCommandIncludePath,
329 "directory/input1.aidl",
330 "directory/input2.aidl",
331 "directory/input3.aidl",
332 nullptr,
333 };
334 EXPECT_EQ(false, GetOptions(arg_with_no_out_dir)->Ok());
335 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
336 }
337
TEST(OptionsTests,ParsesCompileRustInvalid_RejectHeaderOut)338 TEST(OptionsTests, ParsesCompileRustInvalid_RejectHeaderOut) {
339 string expected_error = "Header output directory is set, which does not make sense for Rust.";
340 CaptureStderr();
341 // -h options is not for Rust
342 const char* arg_with_header_dir[] = {
343 "aidl", "--lang=rust", kCompileCommandIncludePath, "-o src_out",
344 "-h header_out", "directory/input1.aidl", "directory/input2.aidl", "directory/input3.aidl",
345 nullptr,
346 };
347 EXPECT_EQ(false, GetOptions(arg_with_header_dir)->Ok());
348 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr(expected_error));
349 }
350
TEST(OptionsTests,ParsesWarningEnableAll)351 TEST(OptionsTests, ParsesWarningEnableAll) {
352 const char* args[] = {
353 "aidl", "--lang=java", "-Weverything", "--out=out", "input.aidl", nullptr,
354 };
355 auto options = GetOptions(args);
356 EXPECT_TRUE(options->Ok());
357 auto mapping = options->GetDiagnosticMapping();
358 EXPECT_EQ(DiagnosticSeverity::WARNING, mapping.Severity(DiagnosticID::interface_name));
359 }
360
TEST(OptionsTests,ParsesWarningEnableSpecificWarning)361 TEST(OptionsTests, ParsesWarningEnableSpecificWarning) {
362 const char* args[] = {
363 "aidl", "--lang=java", "-Winterface-name", "--out=out", "input.aidl", nullptr,
364 };
365 auto options = GetOptions(args);
366 EXPECT_TRUE(options->Ok());
367 auto mapping = options->GetDiagnosticMapping();
368 EXPECT_EQ(DiagnosticSeverity::WARNING, mapping.Severity(DiagnosticID::interface_name));
369 }
370
TEST(OptionsTests,ParsesWarningDisableSpecificWarning)371 TEST(OptionsTests, ParsesWarningDisableSpecificWarning) {
372 const char* args[] = {
373 "aidl", "--lang=java", "-Weverything", "-Wno-interface-name",
374 "--out=out", "input.aidl", nullptr,
375 };
376 auto options = GetOptions(args);
377 EXPECT_TRUE(options->Ok());
378 auto mapping = options->GetDiagnosticMapping();
379 EXPECT_EQ(DiagnosticSeverity::DISABLED, mapping.Severity(DiagnosticID::interface_name));
380 }
381
TEST(OptionsTests,ParsesWarningAsErrors)382 TEST(OptionsTests, ParsesWarningAsErrors) {
383 const char* args[] = {
384 "aidl", "--lang=java", "-Werror", "-Weverything", "--out=out", "input.aidl", nullptr,
385 };
386 auto options = GetOptions(args);
387 EXPECT_TRUE(options->Ok());
388 auto mapping = options->GetDiagnosticMapping();
389 EXPECT_EQ(DiagnosticSeverity::ERROR, mapping.Severity(DiagnosticID::interface_name));
390 }
391
TEST(OptionsTests,RejectsUnknownWarning)392 TEST(OptionsTests, RejectsUnknownWarning) {
393 const char* args[] = {
394 "aidl", "--lang=java", "-Wfoobar", "--out=out", "input.aidl", nullptr,
395 };
396 CaptureStderr();
397 auto options = GetOptions(args);
398 EXPECT_FALSE(options->Ok());
399 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr("unknown warning: foobar"));
400 }
401
TEST(OptionsTests,CheckApi)402 TEST(OptionsTests, CheckApi) {
403 const char* args[] = {
404 "aidl", "--checkapi", "old", "new", nullptr,
405 };
406 CaptureStderr();
407 auto options = GetOptions(args);
408 EXPECT_TRUE(options->Ok());
409 EXPECT_EQ("", GetCapturedStderr());
410 EXPECT_EQ(Options::Task::CHECK_API, options->GetTask());
411 EXPECT_EQ(Options::CheckApiLevel::COMPATIBLE, options->GetCheckApiLevel());
412 }
413
TEST(OptionsTests,CheckApiWithCompatible)414 TEST(OptionsTests, CheckApiWithCompatible) {
415 const char* args[] = {
416 "aidl", "--checkapi=compatible", "old", "new", nullptr,
417 };
418 CaptureStderr();
419 auto options = GetOptions(args);
420 EXPECT_TRUE(options->Ok());
421 EXPECT_EQ("", GetCapturedStderr());
422 EXPECT_EQ(Options::Task::CHECK_API, options->GetTask());
423 EXPECT_EQ(Options::CheckApiLevel::COMPATIBLE, options->GetCheckApiLevel());
424 }
425
TEST(OptionsTests,CheckApiWithEqual)426 TEST(OptionsTests, CheckApiWithEqual) {
427 const char* args[] = {
428 "aidl", "--checkapi=equal", "old", "new", nullptr,
429 };
430 CaptureStderr();
431 auto options = GetOptions(args);
432 EXPECT_TRUE(options->Ok());
433 EXPECT_EQ("", GetCapturedStderr());
434 EXPECT_EQ(Options::Task::CHECK_API, options->GetTask());
435 EXPECT_EQ(Options::CheckApiLevel::EQUAL, options->GetCheckApiLevel());
436 }
437
TEST(OptionsTests,CheckApiWithUnknown)438 TEST(OptionsTests, CheckApiWithUnknown) {
439 const char* args[] = {
440 "aidl", "--checkapi=unknown", "old", "new", nullptr,
441 };
442 CaptureStderr();
443 auto options = GetOptions(args);
444 EXPECT_FALSE(options->Ok());
445 EXPECT_THAT(GetCapturedStderr(), testing::HasSubstr("Unsupported --checkapi level: 'unknown'"));
446 }
447
448 } // namespace aidl
449 } // namespace android
450