Skip to content

Add basic platform support for Android. #653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ extension Array where Element == PackageDescription.SwiftSetting {

.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),

.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi])),
.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
.define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .windows, .wasi])),
.define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])),
]
Expand Down
6 changes: 3 additions & 3 deletions Sources/Testing/ABI/EntryPoints/EntryPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ extension Event.ConsoleOutputRecorder.Options {
/// Whether or not the system terminal claims to support 16-color ANSI escape
/// codes.
private static var _terminalSupports16ColorANSIEscapeCodes: Bool {
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
if let termVariable = Environment.variable(named: "TERM") {
return termVariable != "dumb"
}
Expand All @@ -673,7 +673,7 @@ extension Event.ConsoleOutputRecorder.Options {
/// Whether or not the system terminal claims to support 256-color ANSI escape
/// codes.
private static var _terminalSupports256ColorANSIEscapeCodes: Bool {
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
if let termVariable = Environment.variable(named: "TERM") {
return strstr(termVariable, "256") != nil
}
Expand All @@ -695,7 +695,7 @@ extension Event.ConsoleOutputRecorder.Options {
/// Whether or not the system terminal claims to support true-color ANSI
/// escape codes.
private static var _terminalSupportsTrueColorANSIEscapeCodes: Bool {
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
if let colortermVariable = Environment.variable(named: "COLORTERM") {
return strstr(colortermVariable, "truecolor") != nil
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/ABI/EntryPoints/SwiftPMEntryPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private import _TestingInternals
///
/// This constant is not part of the public interface of the testing library.
var EXIT_NO_TESTS_FOUND: CInt {
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
EX_UNAVAILABLE
#elseif os(Windows)
CInt(ERROR_NOT_FOUND)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Testing/Events/Recorder/Event.Symbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension Event.Symbol {
/// be used to represent it in text-based output. The value of this property
/// is platform-dependent.
public var unicodeCharacter: Character {
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
switch self {
case .default:
// Unicode: WHITE DIAMOND
Expand Down
4 changes: 4 additions & 0 deletions Sources/Testing/SourceAttribution/Backtrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public struct Backtrace: Sendable {
}
#elseif os(Linux)
initializedCount = .init(backtrace(addresses.baseAddress!, .init(addresses.count)))
#elseif os(Android)
addresses.withMemoryRebound(to: UnsafeMutableRawPointer.self) { addresses in
initializedCount = .init(backtrace(addresses.baseAddress!, .init(addresses.count)))
}
#elseif os(Windows)
initializedCount = Int(RtlCaptureStackBackTrace(0, ULONG(addresses.count), addresses.baseAddress!, nil))
#elseif os(WASI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ extension CommandLine {
}
}
return result!
#elseif os(Linux)
#elseif os(Linux) || os(Android)
return try withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(PATH_MAX) * 2) { buffer in
let readCount = readlink("/proc/\(getpid())/exe", buffer.baseAddress!, buffer.count - 1)
let readCount = readlink("/proc/self/exe", buffer.baseAddress!, buffer.count - 1)
guard readCount >= 0 else {
throw CError(rawValue: swt_errno())
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Testing/Support/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ enum Environment {
}
}

#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
/// Get all environment variables from a POSIX environment block.
///
/// - Parameters:
Expand Down Expand Up @@ -103,7 +103,7 @@ enum Environment {
}
#endif
return _get(fromEnviron: _NSGetEnviron()!.pointee!)
#elseif os(Linux)
#elseif os(Linux) || os(Android)
_get(fromEnviron: swt_environ())
#elseif os(WASI)
_get(fromEnviron: __wasilibc_get_environ())
Expand Down Expand Up @@ -170,7 +170,7 @@ enum Environment {
}
return nil
}
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
getenv(name).flatMap { String(validatingCString: $0) }
#elseif os(Windows)
name.withCString(encodedAs: UTF16.self) { name in
Expand Down
10 changes: 5 additions & 5 deletions Sources/Testing/Support/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ struct FileHandle: ~Copyable, Sendable {
/// descriptor, `nil` is passed to `body`.
borrowing func withUnsafePOSIXFileDescriptor<R>(_ body: (CInt?) throws -> R) rethrows -> R {
try withUnsafeCFILEHandle { handle in
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
let fd = fileno(handle)
#elseif os(Windows)
let fd = _fileno(handle)
Expand Down Expand Up @@ -200,7 +200,7 @@ struct FileHandle: ~Copyable, Sendable {
/// other threads.
borrowing func withLock<R>(_ body: () throws -> R) rethrows -> R {
try withUnsafeCFILEHandle { handle in
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
flockfile(handle)
defer {
funlockfile(handle)
Expand Down Expand Up @@ -235,7 +235,7 @@ extension FileHandle {
// If possible, reserve enough space in the resulting buffer to contain
// the contents of the file being read.
var size: Int?
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
withUnsafePOSIXFileDescriptor { fd in
var s = stat()
if let fd, 0 == fstat(fd, &s) {
Expand Down Expand Up @@ -373,7 +373,7 @@ extension FileHandle {
extension FileHandle {
/// Is this file handle a TTY or PTY?
var isTTY: Bool {
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
// If stderr is a TTY and TERM is set, that's good enough for us.
withUnsafePOSIXFileDescriptor { fd in
if let fd, 0 != isatty(fd), let term = Environment.variable(named: "TERM"), !term.isEmpty {
Expand All @@ -399,7 +399,7 @@ extension FileHandle {

/// Is this file handle a pipe or FIFO?
var isPipe: Bool {
#if SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
withUnsafePOSIXFileDescriptor { fd in
guard let fd else {
return false
Expand Down
8 changes: 5 additions & 3 deletions Sources/Testing/Support/GetSymbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal import _TestingInternals
#if !SWT_NO_DYNAMIC_LINKING

/// The platform-specific type of a loaded image handle.
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
typealias ImageAddress = UnsafeMutableRawPointer
#elseif os(Windows)
typealias ImageAddress = HMODULE
Expand All @@ -30,7 +30,9 @@ typealias ImageAddress = Never
/// declare a wrapper function in the internal module's Stubs.h file.
#if SWT_TARGET_OS_APPLE
private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: -2)
#elseif os(Linux)
#elseif os(Android) && _pointerBitWidth(_32)
private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: 0xFFFFFFFF)
#elseif os(Linux) || os(Android)
private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: 0)
#endif

Expand All @@ -57,7 +59,7 @@ private nonisolated(unsafe) let RTLD_DEFAULT = ImageAddress(bitPattern: 0)
/// calling `EnumProcessModules()` and iterating over the returned handles
/// looking for one containing the given function.
func symbol(in handle: ImageAddress? = nil, named symbolName: String) -> UnsafeRawPointer? {
#if SWT_TARGET_OS_APPLE || os(Linux)
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android)
dlsym(handle ?? RTLD_DEFAULT, symbolName).map(UnsafeRawPointer.init)
#elseif os(Windows)
symbolName.withCString { symbolName in
Expand Down
10 changes: 5 additions & 5 deletions Sources/Testing/Support/Locked.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
/// To keep the implementation of this type as simple as possible,
/// `pthread_mutex_t` is used on Apple platforms instead of `os_unfair_lock`
/// or `OSAllocatedUnfairLock`.
#if SWT_TARGET_OS_APPLE || os(Linux) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
private typealias _Lock = pthread_mutex_t
#elseif os(Windows)
private typealias _Lock = SRWLOCK
Expand All @@ -52,7 +52,7 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
private final class _Storage: ManagedBuffer<T, _Lock> {
deinit {
withUnsafeMutablePointerToElements { lock in
#if SWT_TARGET_OS_APPLE || os(Linux) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
_ = pthread_mutex_destroy(lock)
#elseif os(Windows)
// No deinitialization needed.
Expand All @@ -71,7 +71,7 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
init(rawValue: T) {
_storage = _Storage.create(minimumCapacity: 1, makingHeaderWith: { _ in rawValue })
_storage.withUnsafeMutablePointerToElements { lock in
#if SWT_TARGET_OS_APPLE || os(Linux) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
_ = pthread_mutex_init(lock, nil)
#elseif os(Windows)
InitializeSRWLock(lock)
Expand Down Expand Up @@ -101,7 +101,7 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
/// concurrency tools.
nonmutating func withLock<R>(_ body: (inout T) throws -> R) rethrows -> R {
try _storage.withUnsafeMutablePointers { rawValue, lock in
#if SWT_TARGET_OS_APPLE || os(Linux) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
_ = pthread_mutex_lock(lock)
defer {
_ = pthread_mutex_unlock(lock)
Expand All @@ -121,7 +121,7 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
}
}

#if SWT_TARGET_OS_APPLE || os(Linux) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
#if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
/// Acquire the lock and invoke a function while it is held, yielding both the
/// protected value and a reference to the lock itself.
///
Expand Down
23 changes: 23 additions & 0 deletions Sources/Testing/Support/Versions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ let operatingSystemVersion: String = {
return "\(release) (\(version))"
}
}
#elseif os(Android)
if let version = systemProperty(named: "ro.build.version.release") {
return "Android \(version)"
}
#elseif os(Windows)
// See if we can query the kernel directly, bypassing the fake-out logic added
// in Windows 10 and later that misreports the OS version. GetVersionExW()
Expand Down Expand Up @@ -170,3 +174,22 @@ func sysctlbyname(_ name: String, as _: String.Type) -> String? {
}
}
#endif

#if os(Android)
/// Get the Android system property with the given name.
///
/// - Parameters:
/// - name: The name of the system property to get.
///
/// - Returns: The value of the requested system property, or `nil` if it could
/// not be read or could not be converted to a string.
func systemProperty(named name: String) -> String? {
withUnsafeTemporaryAllocation(of: CChar.self, capacity: Int(PROP_VALUE_MAX)) { buffer in
let length = __system_property_get(name, buffer.baseAddress!)
if length > 0 {
return String(validatingCString: buffer.baseAddress!)
}
return nil
}
}
#endif
3 changes: 2 additions & 1 deletion Sources/Testing/Traits/Tags/Tag.Color+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ var swiftTestingDirectoryPath: String? {
if let homeDirectoryPath = _homeDirectoryPath {
return appendPathComponent(swiftTestingDirectoryName, to: homeDirectoryPath)
}
#elseif SWT_TARGET_OS_APPLE
#elseif SWT_TARGET_OS_APPLE || os(Android)
// Other Apple/Darwin platforms do not support the concept of a home
// directory. One exists for the current user, but it's not something that
// actually contains user-configurable data like a .swift-testing directory.
// Android also does not support per-user home directories (does it?)
#elseif os(Windows)
if let appDataDirectoryPath = _appDataDirectoryPath {
return appendPathComponent(swiftTestingDirectoryName, to: appDataDirectoryPath)
Expand Down
2 changes: 1 addition & 1 deletion Sources/_TestingInternals/Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ static void enumerateTypeMetadataSections(const SectionEnumerator& body) {
}
}

#elif defined(__linux__) || defined(_WIN32) || defined(__wasi__)
#elif defined(__linux__) || defined(_WIN32) || defined(__wasi__) || defined(__ANDROID__)
#pragma mark - Linux/Windows implementation

/// Specifies the address range corresponding to a section.
Expand Down
5 changes: 5 additions & 0 deletions Sources/_TestingInternals/include/Includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,9 @@
#include <Psapi.h>
#endif

#if defined(__ANDROID__)
#pragma clang module import posix_filesystem.linux_stat
#include <sys/system_properties.h>
#endif

#endif
4 changes: 3 additions & 1 deletion Sources/_TestingInternals/include/Stubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static LANGID swt_MAKELANGID(int p, int s) {
}
#endif

#if defined(__linux__)
#if defined(__linux__) || defined(__ANDROID__)
/// The environment block.
///
/// By POSIX convention, the environment block variable is declared in client
Expand All @@ -97,6 +97,7 @@ static char *_Nullable *_Null_unspecified swt_environ(void) {
SWT_IMPORT_FROM_STDLIB int pthread_setname_np(pthread_t, const char *);
#endif

#if !defined(__ANDROID__)
#if __has_include(<signal.h>) && defined(si_pid)
/// Get the value of the `si_pid` field of a `siginfo_t` structure.
///
Expand All @@ -120,6 +121,7 @@ static int swt_siginfo_t_si_status(const siginfo_t *siginfo) {
return siginfo->si_status;
}
#endif
#endif

#if defined(__wasi__)
/// Get the version of the C standard library and runtime used by WASI, if
Expand Down
2 changes: 1 addition & 1 deletion Tests/TestingTests/ABIEntryPointTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ struct ABIEntryPointTests {
passing arguments: __CommandLineArguments_v0,
recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void = { _ in }
) async throws -> Bool {
#if !os(Linux) && !SWT_NO_DYNAMIC_LINKING
#if !os(Linux) && !os(Android) && !SWT_NO_DYNAMIC_LINKING
// Get the ABI entry point by dynamically looking it up at runtime.
//
// NOTE: The standard Linux linker does not allow exporting symbols from
Expand Down
2 changes: 1 addition & 1 deletion Tests/TestingTests/Support/EnvironmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ extension Environment {
environment[name] = value
}
return true
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(WASI)
#elseif SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || os(WASI)
if let value {
return 0 == setenv(name, value, 1)
}
Expand Down
4 changes: 3 additions & 1 deletion Tests/TestingTests/Support/FileHandleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private import _TestingInternals
#if !SWT_NO_FILE_IO
// NOTE: we don't run these tests on iOS (etc.) because processes on those
// platforms are sandboxed and do not have arbitrary filesystem access.
#if os(macOS) || os(Linux) || os(Windows)
#if os(macOS) || os(Linux) || os(Android) || os(Windows)
@Suite("FileHandle Tests")
struct FileHandleTests {
// FileHandle is non-copyable, so it cannot yet be used as a test parameter.
Expand Down Expand Up @@ -226,6 +226,8 @@ func temporaryDirectory() throws -> String {
}
#elseif os(Linux)
"/tmp"
#elseif os(Android)
Environment.variable(named: "TMPDIR") ?? "/data/local/tmp"
#elseif os(Windows)
try withUnsafeTemporaryAllocation(of: wchar_t.self, capacity: Int(MAX_PATH + 1)) { buffer in
// NOTE: GetTempPath2W() was introduced in Windows 10 Build 20348.
Expand Down
2 changes: 1 addition & 1 deletion cmake/modules/shared/CompilerSettings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ add_compile_options(
if(APPLE)
add_compile_definitions("SWT_TARGET_OS_APPLE")
endif()
set(SWT_NO_EXIT_TESTS_LIST "iOS" "watchOS" "tvOS" "visionOS" "WASI")
set(SWT_NO_EXIT_TESTS_LIST "iOS" "watchOS" "tvOS" "visionOS" "WASI" "Android")
if(CMAKE_SYSTEM_NAME IN_LIST SWT_NO_EXIT_TESTS_LIST)
add_compile_definitions("SWT_NO_EXIT_TESTS")
endif()
Expand Down