Skip to content

Commit

Permalink
Windows, test wrapper: Zip entry list creation
Browse files Browse the repository at this point in the history
Implement a function that can create a list of zip
file entries. We'll use the list with
devtools_ijar::ZipBuilder, to archive undeclared
outputs from tests.

See #5508

Change-Id: Idf703f768641a2827e371069b8511fb6e04adaa4

Closes #6492.

Change-Id: Idf703f768641a2827e371069b8511fb6e04adaa4
PiperOrigin-RevId: 218496587
  • Loading branch information
laszlocsomor authored and Copybara-Service committed Oct 24, 2018
1 parent 6c17197 commit 8c280bd
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
89 changes: 89 additions & 0 deletions tools/test/windows/tw.cc
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,30 @@ bool GetFileListRelativeTo(
std::wstring(), result);
}

bool ToZipEntryPaths(
const Path& root,
const std::vector<bazel::tools::test_wrapper::FileInfo>& files,
ZipEntryPaths* result) {
std::string acp_root;
if (!WcsToAcp(AsMixedPath(root.Get()), &acp_root)) {
return false;
}

// Convert all UTF-16 paths to ANSI paths.
std::vector<std::string> acp_file_list;
acp_file_list.reserve(files.size());
for (const auto& e : files) {
std::string acp_path;
if (!WcsToAcp(AsMixedPath(e.rel_path), &acp_path)) {
return false;
}
acp_file_list.push_back(acp_path);
}

result->Create(acp_root, acp_file_list);
return true;
}

bool OpenFileForWriting(const std::wstring& path, HANDLE* result) {
*result = CreateFileW(bazel::windows::HasUncPrefix(path.c_str())
? path.c_str()
Expand Down Expand Up @@ -781,6 +805,62 @@ Path Path::Dirname() const {

} // namespace

void ZipEntryPaths::Create(const std::string& root,
const std::vector<std::string>& relative_paths) {
size_t total_size = 0;
for (const auto& e : relative_paths) {
// Increase total size for absolute paths by <root> + "/" + <path> +
// null-terminator.
total_size += root.size() + 1 + e.size() + 1;
}

// Store all absolute paths in one continuous char array.
abs_paths_.reset(new char[total_size]);

// Store pointers in two arrays. The pointers point into `abs_path`.
// We'll pass these paths to devtools_ijar::ZipBuilder::EstimateSize that
// expects an array of char pointers. The last element must be NULL, so
// allocate one extra pointer.
abs_path_ptrs_.reset(new char*[relative_paths.size() + 1]);
entry_path_ptrs_.reset(new char*[relative_paths.size() + 1]);

char* p = abs_paths_.get();
// Create all full paths (root + '/' + relative_paths[i] + '\0').
//
// If `root` is "c:/foo", then store the following:
//
// - Store each absolute path consecutively in `abs_paths_` (via `p`).
// Store paths with forward slashes and not backslashes, because we use them
// as zip entry paths, as well as paths we open with CreateFileA (which can
// convert these paths internally to Windows-style).
// Example: "c:/foo/bar.txt\0c:/foo/sub/baz.txt\0"
//
// - Store pointers in `abs_path_ptrs_`, pointing to the start of each
// string inside `abs_paths_`.
// Example: "c:/foo/bar.txt\0c:/foo/sub/baz.txt\0"
// ^ here ^ here
//
// - Store pointers in `entry_path_ptrs_`, pointing to the start of each
// zip entry path inside `abs_paths_`, which is the part of each path
// that's relative to `root`.
// Example: "c:/foo/bar.txt\0c:/foo/sub/baz.txt\0"
// ^ here ^ here
//
// - Because the ZipBuilder requires that the file paths and zip entry paths
// are null-terminated arrays, we insert an extra null at their ends.
for (size_t i = 0; i < relative_paths.size(); ++i) {
abs_path_ptrs_.get()[i] = p;
strncpy(p, root.c_str(), root.size());
p += root.size();
*p++ = '/';
entry_path_ptrs_.get()[i] = p;
strncpy(p, relative_paths[i].c_str(), relative_paths[i].size() + 1);
p += relative_paths[i].size() + 1;
}
abs_path_ptrs_.get()[relative_paths.size()] = nullptr;
entry_path_ptrs_.get()[relative_paths.size()] = nullptr;
}

int Main(int argc, wchar_t** argv) {
Path argv0;
std::wstring test_path_arg;
Expand Down Expand Up @@ -817,6 +897,15 @@ bool TestOnly_GetFileListRelativeTo(const std::wstring& abs_root,
GetFileListRelativeTo(root, result);
}

bool TestOnly_ToZipEntryPaths(
const std::wstring& abs_root,
const std::vector<bazel::tools::test_wrapper::FileInfo>& files,
ZipEntryPaths* result) {
Path root;
return blaze_util::IsAbsolute(abs_root) && root.Set(abs_root) &&
ToZipEntryPaths(root, files, result);
}

} // namespace testing
} // namespace test_wrapper
} // namespace tools
Expand Down
36 changes: 36 additions & 0 deletions tools/test/windows/tw.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,36 @@ struct FileInfo {
int size;
};

// Zip entry paths for devtools_ijar::ZipBuilder.
// The function signatures mirror the signatures of ZipBuilder's functions.
class ZipEntryPaths {
public:
// Initialize the strings in this object.
// `root` must be an absolute mixed-style path (Windows path with "/"
// separators).
// `files` must be relative, Unix-style paths.
void Create(const std::string& root, const std::vector<std::string>& files);

// Returns a mutable array of const pointers to const char data.
// Each pointer points to an absolute path: the file to archive.
// The pointers are owned by this object and become invalid when the object is
// destroyed.
// Each entry corresponds to the entry at the same index in `EntryPathPtrs`.
char const* const* AbsPathPtrs() const { return abs_path_ptrs_.get(); }

// Returns a mutable array of const pointers to const char data.
// Each pointer points to a relative path: an entry in the zip file.
// The pointers are owned by this object and become invalid when the object is
// destroyed.
// Each entry corresponds to the entry at the same index in `AbsPathPtrs`.
char const* const* EntryPathPtrs() const { return entry_path_ptrs_.get(); }

private:
std::unique_ptr<char[]> abs_paths_;
std::unique_ptr<char*[]> abs_path_ptrs_;
std::unique_ptr<char*[]> entry_path_ptrs_;
};

// The main function of the test wrapper.
int Main(int argc, wchar_t** argv);

Expand All @@ -50,6 +80,12 @@ bool TestOnly_GetEnv(const wchar_t* name, std::wstring* result);
bool TestOnly_GetFileListRelativeTo(const std::wstring& abs_root,
std::vector<FileInfo>* result);

// Converts a list of files to ZIP file entry paths.a
bool TestOnly_ToZipEntryPaths(
const std::wstring& abs_root,
const std::vector<bazel::tools::test_wrapper::FileInfo>& files,
ZipEntryPaths* result);

} // namespace testing

} // namespace test_wrapper
Expand Down
49 changes: 49 additions & 0 deletions tools/test/windows/tw_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <windows.h>

#include <algorithm>
#include <vector>

#include "gtest/gtest.h"
#include "src/main/cpp/util/path_platform.h"
Expand All @@ -31,8 +32,10 @@
namespace {

using bazel::tools::test_wrapper::FileInfo;
using bazel::tools::test_wrapper::ZipEntryPaths;
using bazel::tools::test_wrapper::testing::TestOnly_GetEnv;
using bazel::tools::test_wrapper::testing::TestOnly_GetFileListRelativeTo;
using bazel::tools::test_wrapper::testing::TestOnly_ToZipEntryPaths;

class TestWrapperWindowsTest : public ::testing::Test {
public:
Expand Down Expand Up @@ -85,10 +88,31 @@ void CompareFileInfos(std::vector<FileInfo> actual,
}
}

// According to this StackOverflow post [1] `const` modifies what's on its
// *left*, and "const char" is equivalent to "char const".
// `ZipBuilder::EstimateSize`'s argument type is "char const* const*" meaning a
// mutable array (right *) of const pointers (left *) to const data (char).
//
// [1] https://stackoverflow.com/a/8091846/7778502
void CompareZipEntryPaths(char const* const* actual,
std::vector<const char*> expected, int line) {
for (int i = 0; i < expected.size(); ++i) {
EXPECT_NE(actual[i], nullptr)
<< __FILE__ << "(" << line << "): assertion failed here";
EXPECT_EQ(std::string(actual[i]), std::string(expected[i]))
<< __FILE__ << "(" << line << "): assertion failed here";
}
EXPECT_EQ(actual[expected.size()], nullptr)
<< __FILE__ << "(" << line << "): assertion failed here; value was ("
<< actual[expected.size()] << ")";
}

#define GET_TEST_TMPDIR(result) GetTestTmpdir(result, __LINE__)
#define CREATE_JUNCTION(name, target) CreateJunction(name, target, __LINE__)
#define COMPARE_FILE_INFOS(actual, expected) \
CompareFileInfos(actual, expected, __LINE__)
#define COMPARE_ZIP_ENTRY_PATHS(actual, expected) \
CompareZipEntryPaths(actual, expected, __LINE__)

#define TOSTRING1(x) #x
#define TOSTRING(x) TOSTRING1(x)
Expand Down Expand Up @@ -134,4 +158,29 @@ TEST_F(TestWrapperWindowsTest, TestGetFileListRelativeTo) {
COMPARE_FILE_INFOS(actual, expected);
}

TEST_F(TestWrapperWindowsTest, TestToZipEntryPaths) {
// Pretend we already acquired a file list. The files don't have to exist.
std::wstring root = L"c:\\nul\\root";
std::vector<FileInfo> files = {
{L"foo\\sub\\file1", 0}, {L"foo\\sub\\file2", 5},
{L"foo\\file1", 3}, {L"foo\\file2", 6},
{L"foo\\junc\\file1", 0}, {L"foo\\junc\\file2", 5}};

ZipEntryPaths actual;
ASSERT_TRUE(TestOnly_ToZipEntryPaths(root, files, &actual));

std::vector<const char*> expected_abs_paths = {
"c:/nul/root/foo/sub/file1", "c:/nul/root/foo/sub/file2",
"c:/nul/root/foo/file1", "c:/nul/root/foo/file2",
"c:/nul/root/foo/junc/file1", "c:/nul/root/foo/junc/file2",
};
COMPARE_ZIP_ENTRY_PATHS(actual.AbsPathPtrs(), expected_abs_paths);

std::vector<const char*> expected_entry_paths = {
"foo/sub/file1", "foo/sub/file2", "foo/file1",
"foo/file2", "foo/junc/file1", "foo/junc/file2",
};
COMPARE_ZIP_ENTRY_PATHS(actual.EntryPathPtrs(), expected_entry_paths);
}

} // namespace

0 comments on commit 8c280bd

Please sign in to comment.