mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
libswiftOSLogTestHelper is built with add_swift_target_library, which is used for runtime components, and we only support building those for 13.0+. These back-deploy bots are meant to test against the OS's libraries, since that's what's actually used when back-deploying, but that's not right for this library. resolves rdar://159026163
730 lines
21 KiB
Swift
730 lines
21 KiB
Swift
// RUN: %empty-directory(%t)
|
|
// RUN: %target-build-swift %s -swift-version 5 -DPTR_SIZE_%target-ptrsize -o %t/OSLogExecutionTest
|
|
// RUN: %target-codesign %t/OSLogExecutionTest
|
|
// RUN: %target-run %t/OSLogExecutionTest
|
|
//
|
|
// RUN: %target-build-swift %s -O -swift-version 5 -DPTR_SIZE_%target-ptrsize -o %t/OSLogExecutionTest
|
|
// RUN: %target-codesign %t/OSLogExecutionTest
|
|
// RUN: %target-run %t/OSLogExecutionTest
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: foundation
|
|
//
|
|
// REQUIRES: VENDOR=apple
|
|
// UNSUPPORTED: use_os_stdlib
|
|
// UNSUPPORTED: back_deployment_runtime
|
|
|
|
// Run-time tests for testing the correctness of the optimizations that optimize the
|
|
// construction of the format string and the byte buffer from a string interpolation.
|
|
// The tests here are run with -Onone (which includes only mandatory optimizations)
|
|
// and also with full optimizations -O.
|
|
|
|
import OSLogTestHelper
|
|
import StdlibUnittest
|
|
import Foundation
|
|
|
|
defer { runAllTests() }
|
|
|
|
internal var InterpolationTestSuite = TestSuite("OSLogInterpolationTest")
|
|
internal let bitsPerByte = 8
|
|
|
|
/// A struct that provides methods for checking whether a given byte buffer
|
|
/// conforms to the format expected by the os_log ABI. This struct acts as
|
|
/// a specification of the byte buffer format.
|
|
internal struct OSLogBufferChecker {
|
|
|
|
internal let buffer: UnsafeBufferPointer<UInt8>
|
|
|
|
internal init(_ byteBuffer: UnsafeBufferPointer<UInt8>) {
|
|
buffer = byteBuffer
|
|
}
|
|
|
|
/// Bit mask for setting bits in the preamble. The bits denoted by the bit
|
|
/// mask indicate whether there is an argument that is private, and whether
|
|
/// there is an argument that is non-scalar: String, NSObject or Pointer.
|
|
internal enum PreambleBitMask: UInt8 {
|
|
case privateBitMask = 0x1
|
|
case nonScalarBitMask = 0x2
|
|
}
|
|
|
|
/// Check the summary bytes of the byte buffer.
|
|
/// - Parameters:
|
|
/// - argumentCount: number of arguments expected to be in the buffer.
|
|
/// - hasPrivate: true iff there exists a private argument
|
|
/// - hasNonScalar: true iff there exists a non-scalar argument
|
|
internal func checkSummaryBytes(
|
|
argumentCount: UInt8,
|
|
hasPrivate: Bool,
|
|
hasNonScalar: Bool
|
|
) {
|
|
let preamble = buffer[0]
|
|
let privateBit = preamble & PreambleBitMask.privateBitMask.rawValue
|
|
expectEqual(hasPrivate, privateBit != 0)
|
|
|
|
let nonScalarBit = preamble & PreambleBitMask.nonScalarBitMask.rawValue
|
|
expectEqual(hasNonScalar, nonScalarBit != 0)
|
|
|
|
expectEqual(argumentCount, buffer[1])
|
|
}
|
|
|
|
/// The possible values for the argument flag as defined by the os_log ABI.
|
|
/// This occupies four least significant bits of the first byte of the
|
|
/// argument header. Two least significant bits are used to indicate privacy
|
|
/// and the other two bits are reserved.
|
|
internal enum ArgumentFlag: UInt8 {
|
|
case autoFlag = 0
|
|
case privateFlag = 0x1
|
|
case publicFlag = 0x2
|
|
}
|
|
|
|
/// The possible values for the argument type as defined by the os_log ABI.
|
|
/// This occupies four most significant bits of the first byte of the
|
|
/// argument header.
|
|
internal enum ArgumentType: UInt8 {
|
|
case scalar = 0, count = 1, string = 2, pointer = 3, object = 4, mask = 7
|
|
// TODO: include wide string and errno here if needed.
|
|
}
|
|
|
|
/// Check the encoding of an argument headers in the byte buffer starting from
|
|
/// the `startIndex` and return argument bytes.
|
|
private func checkArgumentHeadersAndGetBytes(
|
|
startIndex: Int,
|
|
size: UInt8,
|
|
flag: ArgumentFlag,
|
|
type: ArgumentType
|
|
) -> [UInt8] {
|
|
let argumentHeader = buffer[startIndex]
|
|
expectEqual((type.rawValue << 4) | flag.rawValue, argumentHeader)
|
|
expectEqual(size, buffer[startIndex + 1])
|
|
// Argument data starts after the two header bytes.
|
|
let argumentBytes: [UInt8] =
|
|
(0..<Int(size)).reduce(into: []) { (acc, index) in
|
|
acc.append(buffer[startIndex + 2 + index])
|
|
}
|
|
return argumentBytes
|
|
}
|
|
|
|
/// Check whether the bytes starting from `startIndex` contain the encoding for a
|
|
/// numerical value.
|
|
internal func checkNumeric<T>(
|
|
startIndex: Int,
|
|
flag: ArgumentFlag,
|
|
type: ArgumentType,
|
|
expectedValue: T
|
|
) where T : Numeric {
|
|
let byteSize = UInt8(MemoryLayout<T>.size)
|
|
let argumentBytes =
|
|
checkArgumentHeadersAndGetBytes(
|
|
startIndex: startIndex,
|
|
size: byteSize,
|
|
flag: flag,
|
|
type: type)
|
|
withUnsafeBytes(of: expectedValue) { expectedBytes in
|
|
for i in 0..<Int(byteSize) {
|
|
expectEqual(
|
|
expectedBytes[i],
|
|
argumentBytes[i],
|
|
"mismatch at byte number \(i) "
|
|
+ "of the expected value \(expectedValue)")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check whether the bytes starting from `startIndex` contain the encoding for an Int.
|
|
internal func checkInt<T>(
|
|
startIndex: Int,
|
|
flag: ArgumentFlag,
|
|
expectedInt: T
|
|
) where T : FixedWidthInteger {
|
|
checkNumeric(
|
|
startIndex: startIndex,
|
|
flag: flag,
|
|
type: .scalar,
|
|
expectedValue: expectedInt)
|
|
}
|
|
|
|
/// Check whether the bytes starting from `startIndex` contain the encoding for a count.
|
|
internal func checkCount(
|
|
startIndex: Int,
|
|
expectedCount: Int,
|
|
precision: Bool
|
|
) {
|
|
checkNumeric(
|
|
startIndex: startIndex,
|
|
flag: .autoFlag,
|
|
type: precision ? .count : .scalar,
|
|
expectedValue: CInt(expectedCount))
|
|
}
|
|
|
|
/// Check whether the bytes starting from `startIndex` contain the encoding
|
|
/// for a string.
|
|
internal func checkString(
|
|
startIndex: Int,
|
|
flag: ArgumentFlag,
|
|
expectedString: String
|
|
) {
|
|
let pointerSize = UInt8(MemoryLayout<UnsafePointer<Int8>>.size)
|
|
let argumentBytes =
|
|
checkArgumentHeadersAndGetBytes(
|
|
startIndex: startIndex,
|
|
size: pointerSize,
|
|
flag: flag,
|
|
type: .string)
|
|
// Read the pointer to a string stored in the buffer and compare it with
|
|
// the expected string using `strcmp`. Note that it is important we use a
|
|
// C function here to compare the string as it more closely represents
|
|
// the C os_log functions.
|
|
var stringAddress: Int = 0
|
|
// Copy the bytes of the address byte by byte. Note that
|
|
// RawPointer.load(fromByteOffset:,_) function cannot be used here as the
|
|
// address: `buffer + offset` is not aligned for reading an Int.
|
|
for i in 0..<Int(pointerSize) {
|
|
stringAddress |= Int(argumentBytes[i]) << (8 * i)
|
|
}
|
|
let bufferDataPointer = UnsafePointer<Int8>(bitPattern: stringAddress)
|
|
expectedString.withCString {
|
|
let compareResult = strcmp($0, bufferDataPointer)
|
|
expectEqual(0, compareResult, "strcmp returned \(compareResult)")
|
|
}
|
|
}
|
|
|
|
/// Check whether the bytes starting from `startIndex` contain the encoding
|
|
/// for an NSObject.
|
|
internal func checkNSObject(
|
|
startIndex: Int,
|
|
flag: ArgumentFlag,
|
|
expectedObject: NSObject
|
|
) {
|
|
let pointerSize = UInt8(MemoryLayout<UnsafePointer<Int8>>.size)
|
|
let argumentBytes =
|
|
checkArgumentHeadersAndGetBytes(
|
|
startIndex: startIndex,
|
|
size: pointerSize,
|
|
flag: flag,
|
|
type: .object)
|
|
// Convert data to a pointer and check if the addresses stored in the
|
|
// pointer and the one in the buffer match.
|
|
let objectAddress =
|
|
Unmanaged
|
|
.passUnretained(expectedObject)
|
|
.toOpaque()
|
|
withUnsafeBytes(of: objectAddress) { expectedBytes in
|
|
for i in 0..<Int(pointerSize) {
|
|
// Argument data starts after the two header bytes.
|
|
expectEqual(
|
|
expectedBytes[i],
|
|
argumentBytes[i],
|
|
"mismatch at byte number \(i) "
|
|
+ "of the expected object address \(objectAddress)")
|
|
}
|
|
}
|
|
}
|
|
|
|
internal func checkDouble(
|
|
startIndex: Int,
|
|
flag: ArgumentFlag,
|
|
expectedValue: Double
|
|
) {
|
|
let byteSize: UInt8 = 8
|
|
let argumentBytes =
|
|
checkArgumentHeadersAndGetBytes(
|
|
startIndex: startIndex,
|
|
size: byteSize,
|
|
flag: flag,
|
|
type: .scalar)
|
|
withUnsafeBytes(of: expectedValue) { expectedBytes in
|
|
for i in 0..<Int(byteSize) {
|
|
expectEqual(
|
|
expectedBytes[i],
|
|
argumentBytes[i],
|
|
"mismatch at byte number \(i) "
|
|
+ "of the expected value \(expectedValue)")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check the given assertions on the arguments stored in the byte buffer.
|
|
/// - Parameters:
|
|
/// - assertions: one assertion for each argument stored in the byte buffer.
|
|
internal func checkArguments(_ assertions: [(Int) -> Void]) {
|
|
var currentArgumentIndex = 2
|
|
|
|
for assertion in assertions {
|
|
expectTrue(currentArgumentIndex < buffer.count)
|
|
|
|
assertion(currentArgumentIndex)
|
|
// Advance the index to the next argument by adding the size of the
|
|
// current argument and the two bytes of headers.
|
|
currentArgumentIndex += 2 + Int(buffer[currentArgumentIndex + 1])
|
|
}
|
|
}
|
|
|
|
/// Check a sequence of assertions on the arguments stored in the byte buffer.
|
|
internal func checkArguments(_ assertions: (Int) -> Void ...) {
|
|
checkArguments(assertions)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("integer literal") {
|
|
_osLogTestHelper(
|
|
"An integer literal \(10)",
|
|
assertion: { (formatString, buffer) in
|
|
expectEqual(
|
|
"An integer literal %ld",
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 1,
|
|
hasPrivate: false,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkInt(startIndex: $0, flag: .autoFlag, expectedInt: 10)
|
|
})
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("integer with formatting") {
|
|
_osLogTestHelper(
|
|
"Minimum integer value: \(UInt.max, format: .hex)",
|
|
assertion: { (formatString, buffer) in
|
|
expectEqual(
|
|
"Minimum integer value: %lx",
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 1,
|
|
hasPrivate: false,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: UInt.max)
|
|
})
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("integer with privacy and formatting") {
|
|
let addr: UInt = 0x7afebabe
|
|
_osLogTestHelper(
|
|
"Access to invalid address: \(addr, format: .hex, privacy: .private)",
|
|
assertion: { (formatString, buffer) in
|
|
expectEqual(
|
|
"Access to invalid address: %{private}lx",
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 1,
|
|
hasPrivate: true,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .privateFlag,
|
|
expectedInt: addr)
|
|
})
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("test multiple arguments") {
|
|
let filePerms: UInt = 0o777
|
|
let pid = 122225
|
|
let privateID = 0x79abcdef
|
|
|
|
_osLogTestHelper(
|
|
"""
|
|
Access prevented: process \(pid, privacy: .public) initiated by \
|
|
user: \(privateID, privacy: .private) attempted resetting \
|
|
permissions to \(filePerms, format: .octal)
|
|
""",
|
|
assertion: { (formatString, buffer) in
|
|
expectEqual(
|
|
"""
|
|
Access prevented: process %{public}ld initiated by \
|
|
user: %{private}ld attempted resetting permissions \
|
|
to %lo
|
|
""",
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 3,
|
|
hasPrivate: true,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments(
|
|
{
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .publicFlag,
|
|
expectedInt: pid)
|
|
},
|
|
{
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .privateFlag,
|
|
expectedInt: privateID)
|
|
},
|
|
{
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: filePerms)
|
|
})
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("interpolation of too many arguments") {
|
|
// The following string interpolation has 49 interpolated values. Only 48
|
|
// of these must be present in the generated format string and byte buffer.
|
|
_osLogTestHelper(
|
|
"""
|
|
\(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \
|
|
\(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \
|
|
\(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \(1) \
|
|
\(1) \(1) \(1) \(1) \(1) \(1) \(1)
|
|
""",
|
|
assertion: { (formatString, buffer) in
|
|
expectEqual(
|
|
String(
|
|
repeating: "%ld ",
|
|
count: Int(maxOSLogArgumentCount)),
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: maxOSLogArgumentCount,
|
|
hasPrivate: false,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments(
|
|
Array(
|
|
repeating: {
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: 1) },
|
|
count: Int(maxOSLogArgumentCount))
|
|
)
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("string interpolations with percents") {
|
|
_osLogTestHelper(
|
|
"a = (c % d)%%",
|
|
assertion: { (formatString, buffer) in
|
|
|
|
expectEqual("a = (c %% d)%%%%", formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 0,
|
|
hasPrivate: false,
|
|
hasNonScalar: false)
|
|
})
|
|
}
|
|
|
|
InterpolationTestSuite.test("integer types") {
|
|
_osLogTestHelper("Int32 max: \(Int32.max)") {
|
|
(formatString, buffer) in
|
|
expectEqual("Int32 max: %d", formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 1,
|
|
hasPrivate: false,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: Int32.max)
|
|
})
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("string arguments") {
|
|
let small = "a"
|
|
let large = "this is a large string"
|
|
_osLogTestHelper(
|
|
"small: \(small, privacy: .public) large: \(large)") {
|
|
(formatString, buffer) in
|
|
expectEqual("small: %{public}s large: %s", formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 2,
|
|
hasPrivate: false,
|
|
hasNonScalar: true
|
|
)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkString(
|
|
startIndex: $0,
|
|
flag: .publicFlag,
|
|
expectedString: small)
|
|
},
|
|
{ bufferChecker.checkString(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedString: large)
|
|
})
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("dynamic strings") {
|
|
let concatString = "hello" + " - " + "world"
|
|
let interpolatedString = "\(31) trillion digits of pi are known so far"
|
|
|
|
_osLogTestHelper(
|
|
"""
|
|
concat: \(concatString, privacy: .public) \
|
|
interpolated: \(interpolatedString, privacy: .private)
|
|
""") { (formatString, buffer) in
|
|
expectEqual("concat: %{public}s interpolated: %{private}s", formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 2,
|
|
hasPrivate: true,
|
|
hasNonScalar: true
|
|
)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkString(
|
|
startIndex: $0,
|
|
flag: .publicFlag,
|
|
expectedString: concatString)
|
|
},
|
|
{ bufferChecker.checkString(
|
|
startIndex: $0,
|
|
flag: .privateFlag,
|
|
expectedString: interpolatedString)
|
|
})
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("NSObject") {
|
|
let nsArray: NSArray = [0, 1, 2]
|
|
let nsDictionary: NSDictionary = [1 : ""]
|
|
|
|
_osLogTestHelper(
|
|
"""
|
|
NSArray: \(nsArray, privacy: .public) \
|
|
NSDictionary: \(nsDictionary)
|
|
""") { (formatString, buffer) in
|
|
expectEqual("NSArray: %{public}@ NSDictionary: %@", formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 2,
|
|
hasPrivate: false,
|
|
hasNonScalar: true
|
|
)
|
|
|
|
bufferChecker.checkArguments({
|
|
bufferChecker.checkNSObject(
|
|
startIndex: $0,
|
|
flag: .publicFlag,
|
|
expectedObject: nsArray)
|
|
},
|
|
{ bufferChecker.checkNSObject(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedObject: nsDictionary)
|
|
})
|
|
}
|
|
}
|
|
|
|
// A generic function.
|
|
func toString<T>(_ subject: T?) -> String {
|
|
return ""
|
|
}
|
|
|
|
protocol TestProto {
|
|
}
|
|
|
|
InterpolationTestSuite.test("Interpolation of complex expressions") {
|
|
class TestClass<T: TestProto>: NSObject {
|
|
func testFunction() {
|
|
// The following call should not crash.
|
|
_osLogTestHelper("A complex expression \(toString(self))") {
|
|
(formatString, _) in
|
|
expectEqual("A complex expression %s", formatString)
|
|
}
|
|
}
|
|
}
|
|
class B : TestProto { }
|
|
TestClass<B>().testFunction()
|
|
}
|
|
|
|
InterpolationTestSuite.test("Include prefix formatting option") {
|
|
let unsignedValue: UInt = 0o171
|
|
_osLogTestHelper(
|
|
"Octal with prefix: \(unsignedValue, format: .octal(includePrefix: true))") {
|
|
(formatString, buffer) in
|
|
expectEqual("Octal with prefix: 0o%lo", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Hex with uppercase formatting option") {
|
|
let unsignedValue: UInt = 0xcafebabe
|
|
_osLogTestHelper(
|
|
"Hex with uppercase: \(unsignedValue, format: .hex(uppercase: true))") {
|
|
(formatString, buffer) in
|
|
expectEqual("Hex with uppercase: %lX", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Integer with explicit positive sign") {
|
|
let posValue = Int.max
|
|
_osLogTestHelper(
|
|
"\(posValue, format: .decimal(explicitPositiveSign: true))") {
|
|
(formatString, buffer) in
|
|
expectEqual("%+ld", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Unsigned integer with explicit positive sign") {
|
|
let posValue = UInt.max
|
|
_osLogTestHelper(
|
|
"\(posValue, format: .decimal(explicitPositiveSign: true))") {
|
|
(formatString, buffer) in
|
|
expectEqual("+%lu", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Integer formatting with precision") {
|
|
let intValue = 1200
|
|
_osLogTestHelper(
|
|
"\(intValue, format: .decimal(minDigits: 10))") {
|
|
(formatString, buffer) in
|
|
expectEqual("%.*ld", formatString)
|
|
|
|
// The buffer should contain two arguments: precision and the actual argument.
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 2,
|
|
hasPrivate: false,
|
|
hasNonScalar: false
|
|
)
|
|
bufferChecker.checkArguments(
|
|
{ bufferChecker.checkCount(
|
|
startIndex: $0,
|
|
expectedCount: 10,
|
|
precision: true)
|
|
},
|
|
{ bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: intValue)
|
|
})
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Integer formatting with alignment") {
|
|
let intValue = 10
|
|
_osLogTestHelper(
|
|
"""
|
|
\(intValue, align: .right(columns: 10)) \
|
|
\(intValue, align: .left(columns: 5), privacy: .private)
|
|
""") {
|
|
(formatString, buffer) in
|
|
expectEqual("%*ld %{private}-*ld", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Integer formatting with precision and alignment") {
|
|
let intValue = 1200
|
|
_osLogTestHelper(
|
|
"\(intValue, format: .decimal(minDigits: 10), align: .left(columns: 7))") {
|
|
(formatString, buffer) in
|
|
expectEqual("%-*.*ld", formatString)
|
|
|
|
// The buffer must contain three arguments: width, precision, and the argument.
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 3,
|
|
hasPrivate: false,
|
|
hasNonScalar: false
|
|
)
|
|
bufferChecker.checkArguments(
|
|
{ bufferChecker.checkCount(
|
|
startIndex: $0,
|
|
expectedCount: 7,
|
|
precision: false)
|
|
},
|
|
{ bufferChecker.checkCount(
|
|
startIndex: $0,
|
|
expectedCount: 10,
|
|
precision: true)
|
|
},
|
|
{ bufferChecker.checkInt(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedInt: intValue)
|
|
})
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("String with alignment") {
|
|
let smallString = "a" + "b"
|
|
let concatString = "hello" + " - " + "world"
|
|
_osLogTestHelper(
|
|
"""
|
|
\(smallString, align: .right(columns: 10)) \
|
|
\(concatString, align: .left(columns: 7), privacy: .public)
|
|
""") {
|
|
(formatString, buffer) in
|
|
expectEqual("%*s %{public}-*s", formatString)
|
|
}
|
|
}
|
|
|
|
InterpolationTestSuite.test("Floats and Doubles") {
|
|
let x = 1.2 + 0.5
|
|
let pi: Double = 3.141593
|
|
let floatPi: Float = 3.141593
|
|
_osLogTestHelper(
|
|
"""
|
|
A double value: \(x, privacy: .private) \
|
|
pi as double: \(pi), pi as float: \(floatPi)
|
|
""") {
|
|
(formatString, buffer) in
|
|
expectEqual(
|
|
"""
|
|
A double value: %{private}f pi as double: %f, \
|
|
pi as float: %f
|
|
""",
|
|
formatString)
|
|
|
|
let bufferChecker = OSLogBufferChecker(buffer)
|
|
bufferChecker.checkSummaryBytes(
|
|
argumentCount: 3,
|
|
hasPrivate: true,
|
|
hasNonScalar: false)
|
|
|
|
bufferChecker.checkArguments(
|
|
{ bufferChecker.checkDouble(
|
|
startIndex: $0,
|
|
flag: .privateFlag,
|
|
expectedValue: x)
|
|
},
|
|
{ bufferChecker.checkDouble(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedValue: pi)
|
|
},
|
|
{ bufferChecker.checkDouble(
|
|
startIndex: $0,
|
|
flag: .autoFlag,
|
|
expectedValue: Double(floatPi))
|
|
})
|
|
}
|
|
}
|