1 /*
2  * Copyright (C) 2018 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 "test/Fixture.h"
18 
19 #include <dirent.h>
20 
21 #include "android-base/errors.h"
22 #include "android-base/file.h"
23 #include "android-base/stringprintf.h"
24 #include "android-base/utf8.h"
25 #include "androidfw/StringPiece.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 
29 #include "cmd/Compile.h"
30 #include "cmd/Link.h"
31 #include "io/FileStream.h"
32 #include "io/Util.h"
33 #include "util/Files.h"
34 
35 using testing::Eq;
36 using testing::Ne;
37 
38 namespace aapt {
39 
40 const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
41 
ClearDirectory(const android::StringPiece & path)42 void ClearDirectory(const android::StringPiece& path) {
43   const std::string root_dir = path.to_string();
44   std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
45   if (!dir) {
46     StdErrDiagnostics().Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
47     return;
48   }
49 
50   while (struct dirent* entry = readdir(dir.get())) {
51     // Do not delete hidden files and do not recurse to the parent of this directory
52     if (util::StartsWith(entry->d_name, ".")) {
53       continue;
54     }
55 
56     std::string full_path = file::BuildPath({root_dir, entry->d_name});
57     if (file::GetFileType(full_path) == file::FileType::kDirectory) {
58       ClearDirectory(full_path);
59 #ifdef _WIN32
60       _rmdir(full_path.c_str());
61 #else
62       rmdir(full_path.c_str());
63 #endif
64     } else {
65       android::base::utf8::unlink(full_path.c_str());
66     }
67   }
68 }
69 
SetUp()70 void TestDirectoryFixture::SetUp() {
71   temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
72                                "_temp",
73                                testing::UnitTest::GetInstance()->current_test_case()->name(),
74                                testing::UnitTest::GetInstance()->current_test_info()->name()});
75   ASSERT_TRUE(file::mkdirs(temp_dir_));
76   ClearDirectory(temp_dir_);
77 }
78 
TearDown()79 void TestDirectoryFixture::TearDown() {
80   ClearDirectory(temp_dir_);
81 }
82 
WriteFile(const std::string & path,const std::string & contents)83 void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
84   CHECK(util::StartsWith(path, temp_dir_))
85       << "Attempting to create a file outside of test temporary directory.";
86 
87   // Create any intermediate directories specified in the path
88   auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
89   if (pos != path.rend()) {
90     std::string dirs = path.substr(0, (&*pos - path.data()));
91     file::mkdirs(dirs);
92   }
93 
94   CHECK(android::base::WriteStringToFile(contents, path));
95 }
96 
CompileFile(const std::string & path,const std::string & contents,const android::StringPiece & out_dir,IDiagnostics * diag)97 bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
98                                      const android::StringPiece& out_dir, IDiagnostics* diag) {
99   WriteFile(path, contents);
100   CHECK(file::mkdirs(out_dir.data()));
101   return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
102 }
103 
Link(const std::vector<std::string> & args,IDiagnostics * diag)104 bool CommandTestFixture::Link(const std::vector<std::string>& args, IDiagnostics* diag) {
105   std::vector<android::StringPiece> link_args;
106   for(const std::string& arg : args) {
107     link_args.emplace_back(arg);
108   }
109 
110   // Link against the android SDK
111   std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
112                                              "integration-tests", "CommandTests",
113                                              "android-28.jar"});
114   link_args.insert(link_args.end(), {"-I", android_sdk});
115 
116   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
117 }
118 
Link(const std::vector<std::string> & args,const android::StringPiece & flat_dir,IDiagnostics * diag)119 bool CommandTestFixture::Link(const std::vector<std::string>& args,
120                               const android::StringPiece& flat_dir, IDiagnostics* diag) {
121   std::vector<android::StringPiece> link_args;
122   for(const std::string& arg : args) {
123     link_args.emplace_back(arg);
124   }
125 
126   // Link against the android SDK
127   std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
128                                              "integration-tests", "CommandTests",
129                                              "android-28.jar"});
130   link_args.insert(link_args.end(), {"-I", android_sdk});
131 
132   // Add the files from the compiled resources directory to the link file arguments
133   Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
134   if (compiled_files) {
135     for (std::string& compile_file : compiled_files.value()) {
136       compile_file = file::BuildPath({flat_dir, compile_file});
137       link_args.emplace_back(std::move(compile_file));
138     }
139   }
140 
141   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
142 }
143 
GetDefaultManifest(const char * package_name)144 std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
145   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
146   WriteFile(manifest_file, android::base::StringPrintf(R"(
147       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
148           package="%s">
149       </manifest>)", package_name));
150   return manifest_file;
151 }
152 
153 std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
154                                                               const android::StringPiece& path) {
155   return apk
156       ->GetFileCollection()
157       ->FindFile(path)
158       ->OpenAsData();
159 }
160 
161 void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
162                                        android::ResXMLTree *out_tree) {
163   ASSERT_THAT(apk, Ne(nullptr));
164 
165   out_tree->setTo(data->data(), data->size());
166   ASSERT_THAT(out_tree->getError(), Eq(android::OK));
167   while (out_tree->next() != android::ResXMLTree::START_TAG) {
168     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
169     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
170   }
171 }
172 
173 } // namespace aapt