//===--- ModuleLoadingTests.cpp -------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "swift/AST/ASTContext.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/Serialization/Validation.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/VirtualFileSystem.h" using namespace swift; static std::string createFilename(StringRef base, StringRef name) { SmallString<256> path = base; llvm::sys::path::append(path, name); return llvm::Twine(path).str(); } static bool emitFileWithContents(StringRef path, StringRef contents, std::string *pathOut = nullptr) { int fd; if (llvm::sys::fs::openFileForWrite(path, fd)) return true; if (pathOut) *pathOut = path.str(); llvm::raw_fd_ostream file(fd, /*shouldClose=*/true); file << contents; return false; } static bool emitFileWithContents(StringRef base, StringRef name, StringRef contents, std::string *pathOut = nullptr) { return emitFileWithContents(createFilename(base, name), contents, pathOut); } namespace unittest { class OpenTrackingFileSystem : public llvm::vfs::ProxyFileSystem { llvm::StringMap numberOfOpensPerFile; public: OpenTrackingFileSystem(llvm::IntrusiveRefCntPtr fs) : llvm::vfs::ProxyFileSystem(fs) {} llvm::ErrorOr> openFileForRead(const Twine &Path) override { numberOfOpensPerFile[Path.str()] += 1; return ProxyFileSystem::openFileForRead(Path); } unsigned numberOfOpens(StringRef path) { return numberOfOpensPerFile[path]; } }; class ModuleInterfaceLoaderTest : public testing::Test { protected: void setupAndLoadModuleInterface() { SmallString<256> tempDir; ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory( "ModuleInterfaceBufferTests.emitModuleInMemory", tempDir)); SWIFT_DEFER { llvm::sys::fs::remove_directories(tempDir); }; auto cacheDir = createFilename(tempDir, "ModuleCache"); ASSERT_FALSE(llvm::sys::fs::create_directory(cacheDir)); auto prebuiltCacheDir = createFilename(tempDir, "PrebuiltModuleCache"); ASSERT_FALSE(llvm::sys::fs::create_directory(prebuiltCacheDir)); // Emit an interface file that we can attempt to compile. ASSERT_FALSE(emitFileWithContents(tempDir, "Library.swiftinterface", "// swift-interface-format-version: 1.0\n" "// swift-module-flags: -module-name TestModule -parse-stdlib\n" "public func foo()\n")); SourceManager sourceMgr; // Create a file system that tracks how many times a file has been opened. llvm::IntrusiveRefCntPtr fs( new OpenTrackingFileSystem(sourceMgr.getFileSystem())); sourceMgr.setFileSystem(fs); PrintingDiagnosticConsumer printingConsumer; DiagnosticEngine diags(sourceMgr); diags.addConsumer(printingConsumer); TypeCheckerOptions typeckOpts; LangOptions langOpts; langOpts.Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); SearchPathOptions searchPathOpts; ClangImporterOptions clangImpOpts; auto ctx = ASTContext::get(langOpts, typeckOpts, searchPathOpts, clangImpOpts, sourceMgr, diags); ctx->addModuleInterfaceChecker( std::make_unique(*ctx, cacheDir, prebuiltCacheDir, ModuleInterfaceLoaderOptions())); auto loader = ModuleInterfaceLoader::create( *ctx, *static_cast( ctx->getModuleInterfaceChecker()), /*dependencyTracker*/nullptr, ModuleLoadingMode::PreferSerialized); Identifier moduleName = ctx->getIdentifier("TestModule"); std::unique_ptr moduleBuffer; std::unique_ptr moduleDocBuffer; std::unique_ptr moduleSourceInfoBuffer; auto error = loader->findModuleFilesInDirectory({moduleName, SourceLoc()}, SerializedModuleBaseName(tempDir, SerializedModuleBaseName("Library")), /*ModuleInterfacePath*/nullptr, &moduleBuffer, &moduleDocBuffer, &moduleSourceInfoBuffer, /*IsFramework*/false); ASSERT_FALSE(error); ASSERT_FALSE(diags.hadAnyError()); ASSERT_NE(nullptr, moduleBuffer); // We should not have written a module doc file. ASSERT_EQ(nullptr, moduleDocBuffer); // Make sure the buffer identifier points to the written module. StringRef cachedModulePath = moduleBuffer->getBufferIdentifier(); ASSERT_TRUE(fs->exists(cachedModulePath)); // Assert that we've only opened this file once, to write it. ASSERT_EQ((unsigned)1, fs->numberOfOpens(cachedModulePath)); auto bufOrErr = fs->getBufferForFile(cachedModulePath); ASSERT_TRUE(bufOrErr); auto bufData = (*bufOrErr)->getBuffer(); auto validationInfo = serialization::validateSerializedAST(bufData); ASSERT_EQ(serialization::Status::Valid, validationInfo.status); ASSERT_EQ(bufData, moduleBuffer->getBuffer()); } }; TEST_F(ModuleInterfaceLoaderTest, LoadModuleFromBuffer) { setupAndLoadModuleInterface(); } } // end namespace unittest