Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 3 additions & 4 deletions .github/actions/perform_unit_tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ runs:
-project firefox-ios/Client.xcodeproj \
-configuration Testing \
-enableCodeCoverage NO \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-destination 'platform=iOS Simulator,name=iPhone 17' \
-clonedSourcePackagesDirPath firefox-ios/SourcePackages \
-derivedDataPath DerivedData \
build-for-testing | xcpretty --color
Expand All @@ -32,8 +32,7 @@ runs:
-project firefox-ios/Client.xcodeproj \
-configuration Testing \
-enableCodeCoverage NO \
-testPlan UnitTest \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-destination 'platform=iOS Simulator,name=iPhone 17' \
-resultBundlePath TestResults.xcresult \
-derivedDataPath DerivedData \
test-without-building | xcpretty --color --report junit --output test_output/xml/report.junit || true
Expand All @@ -48,4 +47,4 @@ runs:
require_tests: false
include_passed: true
detailed_summary: true
fail_on_failure: false
fail_on_failure: false
30 changes: 15 additions & 15 deletions .github/actions/prepare_environment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ runs:
- name: Xcode Select Version
uses: mobiledevops/xcode-select-version-action@v1
with:
xcode-select-version: 16.4
xcode-select-version: 26.3

- name: Check Xcode and Swift Version
shell: bash
Expand All @@ -26,7 +26,7 @@ runs:
shell: bash
run: |
echo "Checking for duplicate SwiftBridging module"
BRIDGING_MODULEMAP="/Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/swift/bridging.modulemap"
BRIDGING_MODULEMAP="/Applications/Xcode_26.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/swift/bridging.modulemap"
if [ -f "$BRIDGING_MODULEMAP" ]; then
echo "Backing up and fixing bridging.modulemap"
sudo cp "$BRIDGING_MODULEMAP" "${BRIDGING_MODULEMAP}.bak"
Expand All @@ -44,9 +44,9 @@ runs:
env:
GITHUB_ACCESS_TOKEN: ${{ inputs.core-token }}

- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12.2'
python-version: '3.12'
cache: 'pip'

- name: Install gems
Expand All @@ -60,9 +60,9 @@ runs:

- name: Restore SPM Cache
id: restore-spm-cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: SourcePackages/
path: firefox-ios/SourcePackages/
key: ${{ runner.os }}-spm-cache-${{ hashFiles('firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}

- name: Install SPM if cache not found
Expand All @@ -72,7 +72,7 @@ runs:

- name: Save SPM Cache if needed
if: steps.restore-spm-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v3
uses: actions/cache/save@v4
with:
path: firefox-ios/SourcePackages/
key: ${{ runner.os }}-spm-cache-${{ hashFiles('firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
Expand All @@ -87,29 +87,29 @@ runs:
echo "Available simulator runtimes:"
xcrun simctl list runtimes

- name: Create and Boot iPhone 16 Simulator
- name: Create and Boot iPhone 17 Simulator
shell: bash
run: |
# Check if simulator already exists
if xcrun simctl list devices | grep -q "iPhone 16"; then
echo "iPhone 16 simulator already exists"
if xcrun simctl list devices | grep -q "iPhone 17"; then
echo "iPhone 17 simulator already exists"
else
echo "Creating iPhone 16 simulator"
echo "Creating iPhone 17 simulator"
# Get the latest available iOS runtime
LATEST_RUNTIME=$(xcrun simctl list runtimes | grep iOS | tail -1 | awk '{print $NF}')
# Get the device type for iPhone 16
DEVICE_TYPE=$(xcrun simctl list devicetypes | grep "iPhone 16" | head -1 | awk '{print $NF}')
# Get the device type for iPhone 17
DEVICE_TYPE=$(xcrun simctl list devicetypes | grep "iPhone 17" | head -1 | awk '{print $NF}')

if [ -n "$LATEST_RUNTIME" ] && [ -n "$DEVICE_TYPE" ]; then
xcrun simctl create "iPhone 16" "$DEVICE_TYPE" "$LATEST_RUNTIME"
xcrun simctl create "iPhone 17" "$DEVICE_TYPE" "$LATEST_RUNTIME"
else
echo "Error: Could not determine runtime or device type"
exit 1
fi
fi

# Boot the simulator
xcrun simctl boot "iPhone 16" || echo "Simulator may already be booted"
xcrun simctl boot "iPhone 17" || echo "Simulator may already be booted"

- name: Setup environment and generate Xcode project with Tuist
shell: bash
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/merge_tests.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
name: Merge Unit Tests

# TEMPORARILY DISABLED: Tests are still crashing and take too long to be useful as a merge gate.
#
# Background — MOB-4320 (PR #1112):
# After the v147 Firefox upstream upgrade, unit tests failed to compile due to Swift 6 strict
# concurrency enforcement (SWIFT_STRICT_CONCURRENCY = minimal is a no-op in Swift 6). PR #1112
# fixed compilation by applying upstream-aligned concurrency patterns (@MainActor, @unchecked
# Sendable, nonisolated(unsafe), etc.) across test and mock files, deleted orphaned test files,
# and updated mocks to match v147 protocol changes. Tests now compile and run — but are still
# failing, which is explicitly deferred to follow-up work.
#
# Pending before re-enabling:
# - Investigate and settle the .xctestplan approach (Tuist-generated vs handcrafted) to avoid stale IDs
# - Fix remaining crashing tests
# - Fix other consistently failing tests
# Re-enable this workflow once the test suite is stable enough to act as a reliable merge gate.
on:
pull_request:
branches: [main, 'patch/**', 'develop']

jobs:
execute_merge_tests:
runs-on: macos-15
if: false # Temporarily disabled — see comment above
runs-on: macos-26
name: Execute Merge Tests

steps:
Expand Down
4 changes: 2 additions & 2 deletions BrowserKit/Sources/Shared/DeferredUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import Foundation

public func deferMaybe<T>(_ s: T) -> Deferred<Maybe<T>> {
public func deferMaybe<T: Sendable>(_ s: T) -> Deferred<Maybe<T>> {
return Deferred(value: Maybe(success: s))
}

Expand All @@ -14,7 +14,7 @@ public func deferMaybe(_ s: String) -> Deferred<Maybe<String>> {
return Deferred(value: Maybe(success: s))
}

public func deferMaybe<T>(_ e: MaybeErrorType) -> Deferred<Maybe<T>> {
public func deferMaybe<T: Sendable>(_ e: MaybeErrorType) -> Deferred<Maybe<T>> {
return Deferred(value: Maybe(failure: e))
}

Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source 'https://rubygems.org'

gem 'danger', :git => 'git@github.com:danger/danger.git', :branch => 'master'
gem 'danger', :git => 'https://github.com/danger/danger.git', :branch => 'master'
gem 'danger-swiftlint'
# Ecosia: add fastlane and plugins
gem 'fastlane', '>= 2.228.0'
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GIT
remote: git@github.com:danger/danger.git
remote: https://github.com/danger/danger.git
revision: 20f1243775da2ce53f55ad5ba65112de5da6e118
branch: master
specs:
Expand Down
2 changes: 1 addition & 1 deletion firefox-ios/Client/Application/WebServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ final class WebServer: WebServerProtocol, @unchecked Sendable {
resource: String,
handler: @escaping @MainActor (
_ request: GCDWebServerRequest?,
_ responseCompletion: @escaping @Sendable (GCDWebServerResponse?) -> Void
_ responseCompletion: @escaping (GCDWebServerResponse?) -> Void
) -> Void
) {
server.addHandler(
Expand Down
2 changes: 2 additions & 0 deletions firefox-ios/Client/Coordinators/Router/Route.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,5 @@ enum Route {
case focusLocationField
}
}

extension Route: Equatable {}
2 changes: 1 addition & 1 deletion firefox-ios/Ecosia/Core/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension Notification.Name {
public static let searchSettingsChanged = Notification.Name("searchSettingsChanged")
}

public struct User: Codable, Equatable {
public struct User: Codable, Equatable, @unchecked Sendable {
/// Mutable static shared instance - marked nonisolated(unsafe) as access is synchronized via DispatchQueue.main
/// Based on [Swift Concurrency Agent Skill](https://github.com/AvdLee/Swift-Concurrency-Agent-Skill)
nonisolated(unsafe) public static var shared = User() {
Expand Down
1 change: 1 addition & 0 deletions firefox-ios/EcosiaTests/BrazeServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import XCTest
@testable import Ecosia
// swiftlint:disable implicitly_unwrapped_optional

@MainActor
final class BrazeServiceTests: XCTestCase {
var brazeService: BrazeService!

Expand Down
2 changes: 1 addition & 1 deletion firefox-ios/EcosiaTests/Core/BookmarkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@testable import Ecosia
import XCTest

final class BookmarkTests: XCTestCase {
final class BookmarkTests: XCTestCase, @unchecked Sendable {
func test_BookmarkSerializer_Parser() async throws {
let input: [BookmarkItem] = [
.bookmark("One", "https://example.com/one", .empty),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ final class MainCookieHandlerTests: XCTestCase {
User.shared.marketCode = .en_us
User.shared.adultFilter = .off
User.shared.autoComplete = true
User.shared.personalized = false
}

override func tearDown() {
Expand Down Expand Up @@ -79,7 +78,6 @@ final class MainCookieHandlerTests: XCTestCase {
User.shared.marketCode = .de_de
User.shared.adultFilter = .strict
User.shared.autoComplete = false
User.shared.personalized = true

let handler = MainCookieHandler(mode: .incognito)
guard let cookie = handler.makeCookie() else {
Expand All @@ -91,7 +89,6 @@ final class MainCookieHandlerTests: XCTestCase {
XCTAssertEqual(values["mc"], "de-de")
XCTAssertEqual(values["f"], "y")
XCTAssertEqual(values["as"], "0")
XCTAssertEqual(values["pz"], "1")
}

// MARK: - Cookie Properties Tests
Expand All @@ -114,7 +111,6 @@ final class MainCookieHandlerTests: XCTestCase {
User.shared.marketCode = .es_cl
User.shared.adultFilter = .moderate
User.shared.autoComplete = false
User.shared.personalized = true

let handler = MainCookieHandler(mode: .standard)
guard let cookie = handler.makeCookie() else {
Expand All @@ -126,7 +122,6 @@ final class MainCookieHandlerTests: XCTestCase {
XCTAssertEqual(values["mc"], "es-cl")
XCTAssertEqual(values["f"], "i")
XCTAssertEqual(values["as"], "0")
XCTAssertEqual(values["pz"], "1")
XCTAssertEqual(values["l"], Language.current.rawValue)
XCTAssertEqual(values["ma"], "1")
XCTAssertEqual(values["mr"], "1")
Expand All @@ -153,7 +148,6 @@ final class MainCookieHandlerTests: XCTestCase {
XCTAssertEqual(User.shared.marketCode, .fr_fr)
XCTAssertEqual(User.shared.adultFilter, .strict)
XCTAssertTrue(User.shared.autoComplete)
XCTAssertFalse(User.shared.personalized)
}

func testReceivedMethodWithInvalidValues() {
Expand Down
4 changes: 2 additions & 2 deletions firefox-ios/EcosiaTests/Core/FavouritesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@testable import Ecosia
import XCTest

final class FavouritesTests: XCTestCase {
final class FavouritesTests: XCTestCase, @unchecked Sendable {

override func setUp() {
try? FileManager.default.removeItem(at: FileManager.pages)
Expand All @@ -28,7 +28,7 @@ final class FavouritesTests: XCTestCase {

func testLoad() {
let expect = expectation(description: "")
var favourites = Favourites()
nonisolated(unsafe) var favourites = Favourites()
favourites.items.append(.init(url: URL(string: "https://www.avocado.com")!, title: "hello world"))
favourites.items.append(.init(url: URL(string: "https://www.guacamole.com")!, title: "lorem ipsum"))
PageStore.queue.async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class FeatureManagementSessiontInitializerTests: XCTestCase {
XCTFail("Initializing session failed: \(error.localizedDescription)")
}

wait(for: [expect], timeout: 1.0)
await fulfillment(of: [expect], timeout: 1.0)
}
}

Expand Down
9 changes: 6 additions & 3 deletions firefox-ios/EcosiaTests/Core/FinancialReportsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ final class FinancialReportsTests: XCTestCase {

try await financialReports.fetchAndUpdate(urlSession: mockURLSession)

XCTAssertEqual(financialReports.latestMonth, Date(timeIntervalSince1970: 1690848000))
XCTAssertEqual(financialReports.latestReport,
let latestMonth = await financialReports.latestMonth
let latestReport = await financialReports.latestReport
XCTAssertEqual(latestMonth, Date(timeIntervalSince1970: 1690848000))
XCTAssertEqual(latestReport,
FinancialReports.Report(totalIncome: 456,
numberOfTreesFinanced: 11))
}
Expand All @@ -40,7 +42,8 @@ final class FinancialReportsTests: XCTestCase {

try await financialReports.fetchAndUpdate(urlSession: mockURLSession)

XCTAssertEqual(financialReports.localizedMonthAndYear, "May 2021")
let localizedMonthAndYear = await financialReports.localizedMonthAndYear
XCTAssertEqual(localizedMonthAndYear, "May 2021")
}
}
// swiftlint:enable implicitly_unwrapped_optional
8 changes: 4 additions & 4 deletions firefox-ios/EcosiaTests/Core/HistoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@testable import Ecosia
import XCTest

final class HistoryTests: XCTestCase {
final class HistoryTests: XCTestCase, @unchecked Sendable {
override func setUp() {
try? FileManager.default.removeItem(at: FileManager.pages)
}
Expand All @@ -27,7 +27,7 @@ final class HistoryTests: XCTestCase {

func testLoad() {
let expect = expectation(description: "")
var history = History()
nonisolated(unsafe) var history = History()
history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world"))
history.add(.init(url: URL(string: "https://guacamole.com")!, title: "lorem ipsum"))
PageStore.queue.async {
Expand All @@ -45,7 +45,7 @@ final class HistoryTests: XCTestCase {

func testDelete() {
let expect = expectation(description: "")
var history = History()
nonisolated(unsafe) var history = History()
history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world"))
PageStore.queue.async {
history = .init()
Expand All @@ -60,7 +60,7 @@ final class HistoryTests: XCTestCase {

func testDeleteAll() {
let expect = expectation(description: "")
var history = History()
nonisolated(unsafe) var history = History()
history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world"))
history.add(.init(url: URL(string: "https://guacamlo.com")!, title: "lorem ipsum"))
PageStore.queue.async {
Expand Down
2 changes: 1 addition & 1 deletion firefox-ios/EcosiaTests/Core/ImagesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import XCTest
@testable import Ecosia
// swiftlint:disable implicitly_unwrapped_optional

final class ImagesTests: XCTestCase {
@MainActor final class ImagesTests: XCTestCase {
private var session: MockURLSession!
private var images: Images!

Expand Down
10 changes: 5 additions & 5 deletions firefox-ios/EcosiaTests/Core/InvestmentsProjectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import XCTest
// Arrange
let date = Date()
let statistics = Statistics.shared
await statistics.setTotalInvestments(123456789)
await statistics.setTotalInvestmentsLastUpdated(date.addingTimeInterval(-100))
await statistics.setInvestmentPerSecond(0.5)
statistics.totalInvestments = 123456789
statistics.totalInvestmentsLastUpdated = date.addingTimeInterval(-100)
statistics.investmentPerSecond = 0.5

// Act
let result = await investmentsProjection.totalInvestedAt(date)
Expand All @@ -31,11 +31,11 @@ import XCTest
func testTimerIsActive() async {
// Arrange
let investmentPerSecond = 1.0
await Statistics.shared.setInvestmentPerSecond(investmentPerSecond)
Statistics.shared.investmentPerSecond = investmentPerSecond

let exp = XCTestExpectation(description: "Wait for timer")
let projection = InvestmentsProjection()
var receivedAmount: Int?
nonisolated(unsafe) var receivedAmount: Int?

// Act
projection.subscribe(self) { amount in
Expand Down
Loading
Loading