Tracing and integration test tweaks (#3336)

* Disable image and document picker integration tests as they randomly fail to load and are flakey.

* Delete any pre-existing log files

* Various tracing tweaks and fixes:
- delete the custom tracing log levels as we can't control their ouput
- implement comparable on them
- change default levels only if the new chosen level increases their verbosity

* Make logging targets mandatory and fix their logging levels

* Switch back to using the `run_tests` reset simulator flag as `fastlane snapshot reset_simulators` was too generic and slow

* Switch all integration test taps to `tapCenter` (nee forceTap) after noticing missed taps on CI.

* Make the logging file prefix explicit, let the main app not use one.

* Rename tracing configuration `target` to `currentTarget`
This commit is contained in:
Stefan Ceriu
2024-09-27 15:08:47 +03:00
committed by GitHub
parent df6d0a999f
commit 9d23dec2e9
16 changed files with 126 additions and 138 deletions
+3 -3
View File
@@ -30,8 +30,8 @@ jobs:
run: run:
source ci_scripts/ci_common.sh && setup_github_actions_environment source ci_scripts/ci_common.sh && setup_github_actions_environment
- name: Reset simulators - name: Delete old log files
run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators run: find '/Users/Shared' -name 'console*' -delete
- name: Run tests - name: Run tests
run: bundle exec fastlane integration_tests run: bundle exec fastlane integration_tests
@@ -44,7 +44,7 @@ jobs:
run: (grep ' TRACE ' /Users/Shared -qR) run: (grep ' TRACE ' /Users/Shared -qR)
- name: Check logs don't contain private messages - name: Check logs don't contain private messages
run: (! grep 'Go down in flames' /Users/Shared -qR) run: "! grep 'Go down in flames' /Users/Shared -R"
- name: Zip results # for faster upload - name: Zip results # for faster upload
if: failure() if: failure()
-3
View File
@@ -35,9 +35,6 @@ jobs:
- name: SwiftFormat - name: SwiftFormat
run: run:
swiftformat --lint . swiftformat --lint .
- name: Reset simulators
run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators
- name: Run tests - name: Run tests
run: bundle exec fastlane unit_tests run: bundle exec fastlane unit_tests
@@ -45,9 +45,6 @@ jobs:
- name: SwiftFormat - name: SwiftFormat
run: swiftformat --lint . run: swiftformat --lint .
- name: Reset simulators
run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators
- name: Run tests - name: Run tests
run: bundle exec fastlane unit_tests skip_previews:true run: bundle exec fastlane unit_tests skip_previews:true
@@ -70,7 +70,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
let appSettings = appHooks.appSettingsHook.configure(AppSettings()) let appSettings = appHooks.appSettingsHook.configure(AppSettings())
MXLog.configure(logLevel: appSettings.logLevel) MXLog.configure(currentTarget: "elementx", filePrefix: nil, logLevel: appSettings.logLevel)
let appName = InfoPlistReader.main.bundleDisplayName let appName = InfoPlistReader.main.bundleDisplayName
let appVersion = InfoPlistReader.main.bundleShortVersionString let appVersion = InfoPlistReader.main.bundleShortVersionString
@@ -9,7 +9,7 @@ import XCTest
extension XCUIElement { extension XCUIElement {
func clearAndTypeText(_ text: String) { func clearAndTypeText(_ text: String) {
forceTap() tapCenter()
guard let currentValue = value as? String else { guard let currentValue = value as? String else {
XCTFail("Tried to clear and type text into a non string value") XCTFail("Tried to clear and type text into a non string value")
@@ -24,7 +24,7 @@ extension XCUIElement {
} }
} }
func forceTap() { func tapCenter() {
let coordinate: XCUICoordinate = coordinate(withNormalizedOffset: .init(dx: 0.5, dy: 0.5)) let coordinate: XCUICoordinate = coordinate(withNormalizedOffset: .init(dx: 0.5, dy: 0.5))
coordinate.tap() coordinate.tap()
} }
+8 -11
View File
@@ -22,21 +22,18 @@ enum MXLog {
private static var didConfigureOnce = false private static var didConfigureOnce = false
private static var rootSpan: Span! private static var rootSpan: Span!
private static var target: String! private static var currentTarget: String!
static func configure(target: String? = nil, static func configure(currentTarget: String,
filePrefix: String?,
logLevel: TracingConfiguration.LogLevel) { logLevel: TracingConfiguration.LogLevel) {
guard !didConfigureOnce else { return } guard !didConfigureOnce else { return }
RustTracing.setup(configuration: .init(logLevel: logLevel, target: target)) RustTracing.setup(configuration: .init(logLevel: logLevel, currentTarget: currentTarget, filePrefix: filePrefix))
if let target { self.currentTarget = currentTarget
self.target = target
} else {
self.target = Constants.target
}
rootSpan = Span(file: #file, line: #line, level: .info, target: self.target, name: "root") rootSpan = Span(file: #file, line: #line, level: .info, target: self.currentTarget, name: "root")
rootSpan.enter() rootSpan.enter()
didConfigureOnce = true didConfigureOnce = true
@@ -138,7 +135,7 @@ enum MXLog {
rootSpan.enter() rootSpan.enter()
} }
return Span(file: file, line: UInt32(line), level: level, target: target, name: name) return Span(file: file, line: UInt32(line), level: level, target: currentTarget, name: name)
} }
// periphery:ignore:parameters function,column,context // periphery:ignore:parameters function,column,context
@@ -157,6 +154,6 @@ enum MXLog {
rootSpan.enter() rootSpan.enter()
} }
logEvent(file: (file as NSString).lastPathComponent, line: UInt32(line), level: level, target: target, message: "\(message)") logEvent(file: (file as NSString).lastPathComponent, line: UInt32(line), level: level, target: currentTarget, message: "\(message)")
} }
} }
@@ -32,7 +32,7 @@ enum RustTracing {
// Log everything on integration tests to check whether // Log everything on integration tests to check whether
// the logs contain any sensitive data. See `UserFlowTests.swift` // the logs contain any sensitive data. See `UserFlowTests.swift`
let filter = if ProcessInfo.isRunningIntegrationTests { let filter = if ProcessInfo.isRunningIntegrationTests {
TracingConfiguration(logLevel: .trace, target: nil).filter TracingConfiguration(logLevel: .trace, currentTarget: "integrationtests", filePrefix: nil).filter
} else { } else {
configuration.filter configuration.filter
} }
@@ -11,9 +11,8 @@ import Collections
// We can filter by level, crate and even file. See more details here: // We can filter by level, crate and even file. See more details here:
// https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples // https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples
struct TracingConfiguration { struct TracingConfiguration {
enum LogLevel: Codable, Hashable { enum LogLevel: String, Codable, Hashable, Comparable {
case error, warn, info, debug, trace case error, warn, info, debug, trace
case custom(String)
var title: String { var title: String {
switch self { switch self {
@@ -27,34 +26,32 @@ struct TracingConfiguration {
return "Debug" return "Debug"
case .trace: case .trace:
return "Trace" return "Trace"
case .custom:
return "Custom"
} }
} }
fileprivate var rawValue: String { static func < (lhs: TracingConfiguration.LogLevel, rhs: TracingConfiguration.LogLevel) -> Bool {
switch self { switch (lhs, rhs) {
case .error: case (.error, _):
return "error" true
case .warn: case (.warn, .error):
return "warn" false
case .info: case (.warn, _):
return "info" true
case .debug: case (.info, .error), (.info, .warn):
return "debug" false
case .trace: case (.info, _):
return "trace" true
case .custom(let filter): case (.debug, .error), (.debug, .warn), (.debug, .info):
return filter false
case (.debug, _):
true
case (.trace, _):
false
} }
} }
} }
enum Target: String { enum Target: String {
case common = ""
case elementx
case hyper, matrix_sdk_ffi, matrix_sdk_crypto case hyper, matrix_sdk_ffi, matrix_sdk_crypto
case matrix_sdk_client = "matrix_sdk::client" case matrix_sdk_client = "matrix_sdk::client"
@@ -66,9 +63,8 @@ struct TracingConfiguration {
case matrix_sdk_ui_timeline = "matrix_sdk_ui::timeline" case matrix_sdk_ui_timeline = "matrix_sdk_ui::timeline"
} }
// The `common` target is excluded because 3rd-party crates might end up logging user data.
static let targets: OrderedDictionary<Target, LogLevel> = [ static let targets: OrderedDictionary<Target, LogLevel> = [
.common: .info, // Never set this lower than info - 3rd-party crates may start logging user data.
.elementx: .info,
.hyper: .warn, .hyper: .warn,
.matrix_sdk_ffi: .info, .matrix_sdk_ffi: .info,
.matrix_sdk_client: .trace, .matrix_sdk_client: .trace,
@@ -92,33 +88,30 @@ struct TracingConfiguration {
/// - Parameter logLevel: the desired log level /// - Parameter logLevel: the desired log level
/// - Parameter target: the name of the target being configured /// - Parameter target: the name of the target being configured
/// - Returns: a custom tracing configuration /// - Returns: a custom tracing configuration
init(logLevel: LogLevel, target: String?) { init(logLevel: LogLevel, currentTarget: String, filePrefix: String?) {
fileName = if let target { fileName = if let filePrefix {
"\(RustTracing.filePrefix)-\(target)" "\(RustTracing.filePrefix)-\(filePrefix)"
} else { } else {
RustTracing.filePrefix RustTracing.filePrefix
} }
if case let .custom(filter) = logLevel {
self.filter = filter
return
}
let overrides = Self.targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in let overrides = Self.targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in
// Keep the defaults here // Keep the defaults here
let ignoredTargets: [Target] = [.common, // Never remove common from the ignored targets (see above for more info). let ignoredTargets: [Target] = [.hyper]
.hyper,
.matrix_sdk_ffi,
.matrix_sdk_oidc,
.matrix_sdk_client,
.matrix_sdk_crypto,
.matrix_sdk_crypto_account,
.matrix_sdk_http_client]
if ignoredTargets.contains(target) { if ignoredTargets.contains(target) {
return return
} }
partialResult[target] = logLevel guard let defaultTargetLogLevel = Self.targets[target] else {
return
}
// Only change the targets that have default values
// smaller than the desired log level
if defaultTargetLogLevel < logLevel {
partialResult[target] = logLevel
}
} }
var newTargets = Self.targets var newTargets = Self.targets
@@ -126,7 +119,7 @@ struct TracingConfiguration {
newTargets.updateValue(logLevel, forKey: target) newTargets.updateValue(logLevel, forKey: target)
} }
let components = newTargets.map { (target: Target, logLevel: LogLevel) in var components = newTargets.map { (target: Target, logLevel: LogLevel) in
guard !target.rawValue.isEmpty else { guard !target.rawValue.isEmpty else {
return logLevel.rawValue return logLevel.rawValue
} }
@@ -134,6 +127,10 @@ struct TracingConfiguration {
return "\(target.rawValue)=\(logLevel.rawValue)" return "\(target.rawValue)=\(logLevel.rawValue)"
} }
// With `common` not being used we manually need to specify the log
// level for passed in targets
components.append("\(currentTarget)=\(logLevel.rawValue)")
filter = components.joined(separator: ",") filter = components.joined(separator: ",")
} }
} }
@@ -135,18 +135,6 @@ struct DeveloperOptionsScreen: View {
private struct LogLevelConfigurationView: View { private struct LogLevelConfigurationView: View {
@Binding var logLevel: TracingConfiguration.LogLevel @Binding var logLevel: TracingConfiguration.LogLevel
@State private var customTracingConfiguration: String
init(logLevel: Binding<TracingConfiguration.LogLevel>) {
_logLevel = logLevel
if case .custom(let configuration) = logLevel.wrappedValue {
customTracingConfiguration = configuration
} else {
customTracingConfiguration = TracingConfiguration(logLevel: .info, target: nil).filter
}
}
var body: some View { var body: some View {
Picker(selection: $logLevel) { Picker(selection: $logLevel) {
ForEach(logLevels, id: \.self) { logLevel in ForEach(logLevels, id: \.self) { logLevel in
@@ -156,24 +144,11 @@ private struct LogLevelConfigurationView: View {
Text("Log level") Text("Log level")
Text("Requires app reboot") Text("Requires app reboot")
} }
if case .custom = logLevel {
TextEditor(text: $customTracingConfiguration)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.onChange(of: customTracingConfiguration) { newValue in
logLevel = .custom(newValue)
}
}
} }
/// Allows the picker to work with associated values /// Allows the picker to work with associated values
private var logLevels: [TracingConfiguration.LogLevel] { private var logLevels: [TracingConfiguration.LogLevel] {
if case let .custom(filter) = logLevel { [.error, .warn, .info, .debug, .trace]
return [.error, .warn, .info, .debug, .trace, .custom(filter)]
} else {
return [.error, .warn, .info, .debug, .trace, .custom("")]
}
} }
} }
@@ -30,7 +30,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate
windowManager.delegate = self windowManager.delegate = self
MXLog.configure(logLevel: .debug) MXLog.configure(currentTarget: "uitests", filePrefix: nil, logLevel: .debug)
ServiceLocator.shared.register(userIndicatorController: UserIndicatorController()) ServiceLocator.shared.register(userIndicatorController: UserIndicatorController())
+10 -11
View File
@@ -12,12 +12,11 @@ extension XCUIApplication {
let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn] let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn]
XCTAssertTrue(getStartedButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(getStartedButton.waitForExistence(timeout: 10.0))
getStartedButton.tap() getStartedButton.tapCenter()
// Get started is network bound, wait for the change homeserver button for longer
let changeHomeserverButton = buttons[A11yIdentifiers.serverConfirmationScreen.changeServer] let changeHomeserverButton = buttons[A11yIdentifiers.serverConfirmationScreen.changeServer]
XCTAssertTrue(changeHomeserverButton.waitForExistence(timeout: 30.0)) XCTAssertTrue(changeHomeserverButton.waitForExistence(timeout: 10.0))
changeHomeserverButton.tap() changeHomeserverButton.tapCenter()
let homeserverTextField = textFields[A11yIdentifiers.changeServerScreen.server] let homeserverTextField = textFields[A11yIdentifiers.changeServerScreen.server]
XCTAssertTrue(homeserverTextField.waitForExistence(timeout: 10.0)) XCTAssertTrue(homeserverTextField.waitForExistence(timeout: 10.0))
@@ -26,7 +25,7 @@ extension XCUIApplication {
let confirmButton = buttons[A11yIdentifiers.changeServerScreen.continue] let confirmButton = buttons[A11yIdentifiers.changeServerScreen.continue]
XCTAssertTrue(confirmButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(confirmButton.waitForExistence(timeout: 10.0))
confirmButton.tap() confirmButton.tapCenter()
// Wait for server confirmation to finish // Wait for server confirmation to finish
let doesNotExistPredicate = NSPredicate(format: "exists == 0") let doesNotExistPredicate = NSPredicate(format: "exists == 0")
@@ -35,7 +34,7 @@ extension XCUIApplication {
let continueButton = buttons[A11yIdentifiers.serverConfirmationScreen.continue] let continueButton = buttons[A11yIdentifiers.serverConfirmationScreen.continue]
XCTAssertTrue(continueButton.waitForExistence(timeout: 30.0)) XCTAssertTrue(continueButton.waitForExistence(timeout: 30.0))
continueButton.tap() continueButton.tapCenter()
let usernameTextField = textFields[A11yIdentifiers.loginScreen.emailUsername] let usernameTextField = textFields[A11yIdentifiers.loginScreen.emailUsername]
XCTAssertTrue(usernameTextField.waitForExistence(timeout: 10.0)) XCTAssertTrue(usernameTextField.waitForExistence(timeout: 10.0))
@@ -51,7 +50,7 @@ extension XCUIApplication {
XCTAssertTrue(nextButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(nextButton.waitForExistence(timeout: 10.0))
XCTAssertTrue(nextButton.isEnabled) XCTAssertTrue(nextButton.isEnabled)
nextButton.tap() nextButton.tapCenter()
// Wait for login to finish // Wait for login to finish
currentTestCase.expectation(for: doesNotExistPredicate, evaluatedWith: usernameTextField) currentTestCase.expectation(for: doesNotExistPredicate, evaluatedWith: usernameTextField)
@@ -63,7 +62,7 @@ extension XCUIApplication {
// Tapping the sheet button while animating upwards fails. Wait for it to settle // Tapping the sheet button while animating upwards fails. Wait for it to settle
sleep(1) sleep(1)
savePasswordButton.tap() savePasswordButton.tapCenter()
} }
// Wait for the home screen to become visible. // Wait for the home screen to become visible.
@@ -80,17 +79,17 @@ extension XCUIApplication {
let profileButton = buttons[A11yIdentifiers.homeScreen.userAvatar] let profileButton = buttons[A11yIdentifiers.homeScreen.userAvatar]
// `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924 // `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924
profileButton.forceTap() profileButton.tapCenter()
// Logout // Logout
let logoutButton = buttons[A11yIdentifiers.settingsScreen.logout] let logoutButton = buttons[A11yIdentifiers.settingsScreen.logout]
XCTAssertTrue(logoutButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(logoutButton.waitForExistence(timeout: 10.0))
logoutButton.tap() logoutButton.tapCenter()
// Confirm logout // Confirm logout
let alertLogoutButton = alerts.firstMatch.buttons["Sign out"] let alertLogoutButton = alerts.firstMatch.buttons["Sign out"]
XCTAssertTrue(alertLogoutButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(alertLogoutButton.waitForExistence(timeout: 10.0))
alertLogoutButton.tap() alertLogoutButton.tapCenter()
// Check that we're back on the login screen // Check that we're back on the login screen
let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn] let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn]
+17 -17
View File
@@ -37,13 +37,13 @@ class UserFlowTests: XCTestCase {
// And open it // And open it
let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch
XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0)) XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0))
firstRoom.tap() firstRoom.tapCenter()
sendMessages() sendMessages()
checkPhotoSharing() // Intentionally disabled as they're super flakey on iOS 18 simulators
// checkPhotoSharing()
checkDocumentSharing() // checkDocumentSharing()
checkLocationSharing() checkLocationSharing()
@@ -57,7 +57,7 @@ class UserFlowTests: XCTestCase {
// Cancel initial the room search // Cancel initial the room search
let searchCancelButton = app.buttons["Cancel"].firstMatch let searchCancelButton = app.buttons["Cancel"].firstMatch
XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0))
searchCancelButton.forceTap() searchCancelButton.tapCenter()
} }
private func sendMessages() { private func sendMessages() {
@@ -67,7 +67,7 @@ class UserFlowTests: XCTestCase {
var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch
XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0))
sendButton.tap() sendButton.tapCenter()
sleep(10) // Wait for the message to be sent sleep(10) // Wait for the message to be sent
@@ -81,12 +81,12 @@ class UserFlowTests: XCTestCase {
sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch
XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0))
sendButton.tap() sendButton.tapCenter()
sleep(5) // Wait for the message to be sent sleep(5) // Wait for the message to be sent
// Close the formatting options // Close the formatting options
app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tap() app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tapCenter()
} }
private func checkPhotoSharing() { private func checkPhotoSharing() {
@@ -98,7 +98,7 @@ class UserFlowTests: XCTestCase {
// Tap on the second image. First one is always broken on simulators. // Tap on the second image. First one is always broken on simulators.
let secondImage = app.scrollViews.images.element(boundBy: 1) let secondImage = app.scrollViews.images.element(boundBy: 1)
XCTAssertTrue(secondImage.waitForExistence(timeout: 20.0)) // Photo library takes a bit to load XCTAssertTrue(secondImage.waitForExistence(timeout: 20.0)) // Photo library takes a bit to load
secondImage.tap() secondImage.tapCenter()
// Wait for the image to be processed and the new screen to appear // Wait for the image to be processed and the new screen to appear
sleep(10) sleep(10)
@@ -129,7 +129,7 @@ class UserFlowTests: XCTestCase {
// Handle map loading errors (missing credentials) // Handle map loading errors (missing credentials)
let alertOkButton = app.alerts.firstMatch.buttons["OK"].firstMatch let alertOkButton = app.alerts.firstMatch.buttons["OK"].firstMatch
if alertOkButton.waitForExistence(timeout: 10.0) { if alertOkButton.waitForExistence(timeout: 10.0) {
alertOkButton.tap() alertOkButton.tapCenter()
} }
allowLocationPermissionOnce() allowLocationPermissionOnce()
@@ -141,7 +141,7 @@ class UserFlowTests: XCTestCase {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let notificationAlertAllowButton = springboard.buttons["Allow Once"].firstMatch let notificationAlertAllowButton = springboard.buttons["Allow Once"].firstMatch
if notificationAlertAllowButton.waitForExistence(timeout: 10.0) { if notificationAlertAllowButton.waitForExistence(timeout: 10.0) {
notificationAlertAllowButton.tap() notificationAlertAllowButton.tapCenter()
} }
} }
@@ -178,7 +178,7 @@ class UserFlowTests: XCTestCase {
// Open the room details // Open the room details
let roomHeader = app.staticTexts[A11yIdentifiers.roomScreen.name] let roomHeader = app.staticTexts[A11yIdentifiers.roomScreen.name]
XCTAssertTrue(roomHeader.waitForExistence(timeout: 10.0)) XCTAssertTrue(roomHeader.waitForExistence(timeout: 10.0))
roomHeader.tap() roomHeader.tapCenter()
// Open the room member details // Open the room member details
tapOnButton(A11yIdentifiers.roomDetailsScreen.people) tapOnButton(A11yIdentifiers.roomDetailsScreen.people)
@@ -186,7 +186,7 @@ class UserFlowTests: XCTestCase {
// Open the first member's details. Loading members for big rooms can take a while. // Open the first member's details. Loading members for big rooms can take a while.
let firstRoomMember = app.scrollViews.buttons.firstMatch let firstRoomMember = app.scrollViews.buttons.firstMatch
XCTAssertTrue(firstRoomMember.waitForExistence(timeout: 1000.0)) XCTAssertTrue(firstRoomMember.waitForExistence(timeout: 1000.0))
firstRoomMember.tap() firstRoomMember.tapCenter()
// Go back to the room member details // Go back to the room member details
tapOnBackButton("People") tapOnBackButton("People")
@@ -206,7 +206,7 @@ class UserFlowTests: XCTestCase {
let profileButton = app.buttons[A11yIdentifiers.homeScreen.userAvatar] let profileButton = app.buttons[A11yIdentifiers.homeScreen.userAvatar]
// `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924 // `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924
profileButton.forceTap() profileButton.tapCenter()
// Open analytics // Open analytics
tapOnButton(A11yIdentifiers.settingsScreen.analytics) tapOnButton(A11yIdentifiers.settingsScreen.analytics)
@@ -233,7 +233,7 @@ class UserFlowTests: XCTestCase {
private func tapOnButton(_ identifier: String, waitForDisappearance: Bool = false) { private func tapOnButton(_ identifier: String, waitForDisappearance: Bool = false) {
let button = app.buttons[identifier] let button = app.buttons[identifier]
XCTAssertTrue(button.waitForExistence(timeout: 10.0)) XCTAssertTrue(button.waitForExistence(timeout: 10.0))
button.tap() button.tapCenter()
if waitForDisappearance { if waitForDisappearance {
let doesNotExistPredicate = NSPredicate(format: "exists == 0") let doesNotExistPredicate = NSPredicate(format: "exists == 0")
@@ -245,7 +245,7 @@ class UserFlowTests: XCTestCase {
private func tapOnMenu(_ identifier: String) { private func tapOnMenu(_ identifier: String) {
let button = app.buttons[identifier] let button = app.buttons[identifier]
XCTAssertTrue(button.waitForExistence(timeout: 10.0)) XCTAssertTrue(button.waitForExistence(timeout: 10.0))
button.forceTap() button.tapCenter()
} }
/// Taps on a back button that the system configured with a label but no identifier. /// Taps on a back button that the system configured with a label but no identifier.
@@ -255,6 +255,6 @@ class UserFlowTests: XCTestCase {
private func tapOnBackButton(_ label: String = "Back") { private func tapOnBackButton(_ label: String = "Back") {
let button = app.buttons.matching(NSPredicate(format: "label == %@ && identifier == ''", label)).firstMatch let button = app.buttons.matching(NSPredicate(format: "label == %@ && identifier == ''", label)).firstMatch
XCTAssertTrue(button.waitForExistence(timeout: 10.0)) XCTAssertTrue(button.waitForExistence(timeout: 10.0))
button.tap() button.tapCenter()
} }
} }
+1 -1
View File
@@ -72,7 +72,7 @@ enum NSELogger {
} }
isConfigured = true isConfigured = true
MXLog.configure(target: "nse", logLevel: logLevel) MXLog.configure(currentTarget: "nse", filePrefix: "nse", logLevel: logLevel)
} }
static func logMemory(with tag: String) { static func logMemory(with tag: String) {
+2 -2
View File
@@ -22,7 +22,7 @@ class LoggingTests: XCTestCase {
let target = "tests" let target = "tests"
XCTAssertTrue(RustTracing.logFiles.isEmpty) XCTAssertTrue(RustTracing.logFiles.isEmpty)
MXLog.configure(target: target, logLevel: .info) MXLog.configure(currentTarget: target, filePrefix: target, logLevel: .info)
// There is something weird with Rust logging where the file writing handle doesn't // There is something weird with Rust logging where the file writing handle doesn't
// notice that the file it is writing to was deleted, so we can't run these checks // notice that the file it is writing to was deleted, so we can't run these checks
@@ -168,7 +168,7 @@ class LoggingTests: XCTestCase {
content: .init(body: "FileString", source: nil, thumbnailSource: nil, contentType: nil)) content: .init(body: "FileString", source: nil, thumbnailSource: nil, contentType: nil))
// When logging that value // When logging that value
MXLog.configure(logLevel: .info) MXLog.configure(currentTarget: "tests", filePrefix: nil, logLevel: .info)
MXLog.info(textMessage) MXLog.info(textMessage)
MXLog.info(noticeMessage) MXLog.info(noticeMessage)
@@ -10,14 +10,35 @@ import XCTest
@testable import ElementX @testable import ElementX
class TracingConfigurationTests: XCTestCase { class TracingConfigurationTests: XCTestCase {
func testConfiguration() { func testConfiguration() { // swiftlint:disable line_length
let configuration = TracingConfiguration(logLevel: .trace, target: nil) var filter = TracingConfiguration(logLevel: .error, currentTarget: "tests", filePrefix: nil).filter
let filterComponents = configuration.filter.components(separatedBy: ",") XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=error")
XCTAssertEqual(filterComponents.first, "info")
XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=trace")) filter = TracingConfiguration(logLevel: .warn, currentTarget: "tests", filePrefix: nil).filter
XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=debug"))
XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=debug")) XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=warn")
XCTAssertTrue(filterComponents.contains("hyper=warn"))
filter = TracingConfiguration(logLevel: .info, currentTarget: "tests", filePrefix: nil).filter
XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=info")
filter = TracingConfiguration(logLevel: .debug, currentTarget: "tests", filePrefix: nil).filter
XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=debug,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=debug,matrix_sdk_base::sliding_sync=debug,matrix_sdk_ui::timeline=debug,tests=debug")
filter = TracingConfiguration(logLevel: .trace, currentTarget: "tests", filePrefix: nil).filter
XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=trace,matrix_sdk::client=trace,matrix_sdk_crypto=trace,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=trace,matrix_sdk::sliding_sync=trace,matrix_sdk_base::sliding_sync=trace,matrix_sdk_ui::timeline=trace,tests=trace")
} // swiftlint:enable line_length
func testLevelOrdering() {
var logLevels: [TracingConfiguration.LogLevel] = [.info, .error, .trace, .debug, .warn]
XCTAssertEqual(logLevels.sorted(), [.error, .warn, .info, .debug, .trace])
logLevels = [.warn, .error, .debug, .trace, .info, .error]
XCTAssertEqual(logLevels.sorted(), [.error, .error, .warn, .info, .debug, .trace])
} }
} }
+10 -5
View File
@@ -80,12 +80,15 @@ lane :alpha do
end end
lane :unit_tests do |options| lane :unit_tests do |options|
reset_simulator = ENV.key?('CI')
run_tests( run_tests(
scheme: "UnitTests", scheme: "UnitTests",
destination: "platform=iOS Simulator,name=iPhone 16,OS=18.0", destination: "platform=iOS Simulator,name=iPhone 16,OS=18.0",
ensure_devices_found: true, ensure_devices_found: true,
result_bundle: true, result_bundle: true,
number_of_retries: 3, number_of_retries: 3,
reset_simulator: reset_simulator
) )
if !options[:skip_previews] if !options[:skip_previews]
@@ -95,6 +98,7 @@ lane :unit_tests do |options|
ensure_devices_found: true, ensure_devices_found: true,
result_bundle: true, result_bundle: true,
number_of_retries: 3, number_of_retries: 3,
reset_simulator: reset_simulator
) )
end end
@@ -102,10 +106,6 @@ lane :unit_tests do |options|
end end
lane :ui_tests do |options| lane :ui_tests do |options|
# Use a fresh simulator state to ensure hardware keyboard isn't attached.
# Not necessary when running on GitHub.
# reset_simulator_contents()
create_simulator_if_necessary( create_simulator_if_necessary(
name: "iPhone 16", name: "iPhone 16",
type: "com.apple.CoreSimulator.SimDeviceType.iPhone-16", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-16",
@@ -124,6 +124,8 @@ lane :ui_tests do |options|
test_to_run = nil test_to_run = nil
end end
reset_simulator = ENV.key?('CI')
run_tests( run_tests(
scheme: "UITests", scheme: "UITests",
devices: ["iPhone 16", "iPad (10th generation)"], devices: ["iPhone 16", "iPad (10th generation)"],
@@ -132,6 +134,7 @@ lane :ui_tests do |options|
result_bundle: true, result_bundle: true,
only_testing: test_to_run, only_testing: test_to_run,
number_of_retries: 3, number_of_retries: 3,
reset_simulator: reset_simulator
) )
end end
@@ -145,12 +148,14 @@ lane :integration_tests do
runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0" runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0"
) )
reset_simulator = ENV.key?('CI')
run_tests( run_tests(
scheme: "IntegrationTests", scheme: "IntegrationTests",
destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0", destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0",
ensure_devices_found: true, ensure_devices_found: true,
result_bundle: true, result_bundle: true,
reset_simulator: true reset_simulator: reset_simulator
) )
end end