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 bool 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   return 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   CHECK(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,const android::StringPiece & flat_dir,IDiagnostics * diag)104 bool CommandTestFixture::Link(const std::vector<std::string>& args,
105                               const android::StringPiece& flat_dir, IDiagnostics* diag) {
106   std::vector<android::StringPiece> link_args;
107   for(const std::string& arg : args) {
108     link_args.emplace_back(arg);
109   }
110 
111   // Link against the android SDK
112   std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
113                                              "integration-tests", "CommandTests",
114                                              "android-28.jar"});
115   link_args.insert(link_args.end(), {"-I", android_sdk});
116 
117   // Add the files from the compiled resources directory to the link file arguments
118   Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
119   if (compiled_files) {
120     for (std::string& compile_file : compiled_files.value()) {
121       compile_file = file::BuildPath({flat_dir, compile_file});
122       link_args.emplace_back(std::move(compile_file));
123     }
124   }
125 
126   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
127 }
128 
GetDefaultManifest(const char * package_name)129 std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
130   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
131   CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"(
132       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
133           package="%s">
134       </manifest>)", package_name)));
135   return manifest_file;
136 }
137 
138 std::unique_ptr<io::IData> CommandTestFixture::OpenFileAsData(LoadedApk* apk,
139                                                               const android::StringPiece& path) {
140   return apk
141       ->GetFileCollection()
142       ->FindFile(path)
143       ->OpenAsData();
144 }
145 
146 void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
147                                        android::ResXMLTree *out_tree) {
148   ASSERT_THAT(apk, Ne(nullptr));
149 
150   out_tree->setTo(data->data(), data->size());
151   ASSERT_THAT(out_tree->getError(), Eq(android::OK));
152   while (out_tree->next() != android::ResXMLTree::START_TAG) {
153     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
154     ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
155   }
156 }
157 
158 } // namespace aapt