<commit_before>import XCTest
@testable import GRDB

class DatabasePoolReleaseMemoryTests: GRDBTestCase {
    
    func testDatabasePoolDeinitClosesAllConnections() throws {
        let countQueue = DispatchQueue(label: "GRDB")
        var openConnectionCount = 0
        var totalOpenConnectionCount = 0
        
        dbConfiguration.SQLiteConnectionDidOpen = {
            countQueue.sync {
                totalOpenConnectionCount += 1
                openConnectionCount += 1
            }
        }
        
        dbConfiguration.SQLiteConnectionDidClose = {
            countQueue.sync {
                openConnectionCount -= 1
            }
        }
        
        // write & read
        
        do {
            // Create and release DatabasePool
            let dbPool = try makeDatabasePool()
            // Writer connection
            try dbPool.write { db in
                try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
            }
            // Reader connection
            try dbPool.read { _ in }
        }
        
        // One reader, one writer
        XCTAssertEqual(totalOpenConnectionCount, 2)
        
        // All connections are closed
        XCTAssertEqual(openConnectionCount, 0)
    }

#if os(iOS)
    func testDatabasePoolReleasesMemoryOnPressureEvent() throws {
        // Create a database pool, and expect a reader connection to be closed
        let expectation = self.expectation(description: "Reader connection closed")
        
        var configuration = Configuration()
        configuration.SQLiteConnectionWillClose = { conn in
            if sqlite3_db_readonly(conn, nil) != 0 {
                expectation.fulfill()
            }
        }
        let dbPool = try makeDatabasePool(configuration: configuration)
        
        // Precondition: there is one reader.
        try dbPool.read { _ in }
        
        // Simulate memory warning.
        NotificationCenter.default.post(name: UIApplication.didReceiveMemoryWarningNotification, object: nil)
        
        // Postcondition: reader connection was closed
        withExtendedLifetime(dbPool) { _ in
            waitForExpectations(timeout: 0.5)
        }
    }

    func testDatabasePoolDoesNotReleaseMemoryOnPressureEventIfDisabled() throws {
        // Create a database pool, and do not expect any reader connection to be closed
        let expectation = self.expectation(description: "Reader connection closed")
        expectation.isInverted = true
        
        var configuration = Configuration()
        configuration.automaticMemoryManagement = false
        configuration.SQLiteConnectionWillClose = { conn in
            if sqlite3_db_readonly(conn, nil) != 0 {
                expectation.fulfill()
            }
        }
        let dbPool = try makeDatabasePool(configuration: configuration)
        
        // Precondition: there is one reader.
        try dbPool.read { _ in }
        
        // Simulate memory warning.
        NotificationCenter.default.post(name: UIApplication.didReceiveMemoryWarningNotification, object: nil)
        
        // Postcondition: no reader connection was closed
        withExtendedLifetime(dbPool) { _ in
            waitForExpectations(timeout: 0.5)
        }
    }
    
    // Regression test for <https://github.com/groue/GRDB.swift/pull/1253#issuecomment-1177166630>
    func testDatabasePoolDoesNotPreventConcurrentReadsOnPressureEvent() throws {
        let dbPool = try makeDatabasePool()
        
        // Start a read that blocks
        let semaphore = DispatchSemaphore(value: 0)
        dbPool.asyncRead { _ in
            semaphore.wait()
        }
        
        // Simulate memory warning.
        NotificationCenter.default.post(name: UIApplication.didReceiveMemoryWarningNotification, object: nil)<commit_msg>Fix compilation error on iOS Xcode 13.3.1.app, macOS 12, Swift 5.6<commit_after>import XCTest
@testable import GRDB

class DatabasePoolReleaseMemoryTests: GRDBTestCase {
    
    func testDatabasePoolDeinitClosesAllConnections() throws {
        let countQueue = DispatchQueue(label: "GRDB")
        var openConnectionCount = 0
        var totalOpenConnectionCount = 0
        
        dbConfiguration.SQLiteConnectionDidOpen = {
            countQueue.sync {
                totalOpenConnectionCount += 1
                openConnectionCount += 1
            }
        }
        
        dbConfiguration.SQLiteConnectionDidClose = {
            countQueue.sync {
                openConnectionCount -= 1
            }
        }
        
        // write & read
        
        do {
            // Create and release DatabasePool
            let dbPool = try makeDatabasePool()
            // Writer connection
            try dbPool.write { db in
                try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
            }
            // Reader connection
            try dbPool.read { _ in }
        }
        
        // One reader, one writer
        XCTAssertEqual(totalOpenConnectionCount, 2)
        
        // All connections are closed
        XCTAssertEqual(openConnectionCount, 0)
    }

#if os(iOS)
    func testDatabasePoolReleasesMemoryOnPressureEvent() throws {
        // Create a database pool, and expect a reader connection to be closed
        let expectation = self.expectation(description: "Reader connection closed")
        
        var configuration = Configuration()
        configuration.SQLiteConnectionWillClose = { conn in
            if sqlite3_db_readonly(conn, nil) != 0 {
                expectation.fulfill()
            }
        }
        let dbPool = try makeDatabasePool(configuration: configuration)
        
        // Precondition: there is one reader.
        try dbPool.read { _ in }
        
        // Simulate memory warning.
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: "UIApplicationDidReceiveMemoryWarningNotification"),
            object: nil)
        
        // Postcondition: reader connection was closed
        withExtendedLifetime(dbPool) { _ in
            waitForExpectations(timeout: 0.5)
        }
    }

    func testDatabasePoolDoesNotReleaseMemoryOnPressureEventIfDisabled() throws {
        // Create a database pool, and do not expect any reader connection to be closed
        let expectation = self.expectation(description: "Reader connection closed")
        expectation.isInverted = true
        
        var configuration = Configuration()
        configuration.automaticMemoryManagement = false
        configuration.SQLiteConnectionWillClose = { conn in
            if sqlite3_db_readonly(conn, nil) != 0 {
                expectation.fulfill()
            }
        }
        let dbPool = try makeDatabasePool(configuration: configuration)
        
        // Precondition: there is one reader.
        try dbPool.read { _ in }
        
        // Simulate memory warning.
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: "UIApplicationDidReceiveMemoryWarningNotification"),
            object: nil)
        
        // Postcondition: no reader connection was closed
        withExtendedLifetime(dbPool) { _ in
            waitForExpectations(timeout: 0.5)
        }
    }
    
    // Regression test for <https://github.com/groue/GRDB.swift/pull/1253#issuecomment-1177166630>
    func testDatabasePoolDoesNotPreventConcurrentReadsOnPressureEvent() throws {
        let dbPool = try makeDatabasePool()
        
        // Start a read that blocks
        let semaphore = DispatchSemaphore(value: 0)
        dbPool.asyncRead { _ in
            semaphore.wait()
        }
        
        // Simulate memory warning.
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: "UIApplicationDidReceiveMemoryWarningNotification"),
            object: nil)
{'fraction_non_alphanumeric': 0.06746835443037974, 'fraction_numerical': 0.008607594936708861, 'mean_word_length': 1.9492347891004107, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3516297', 'n_tokens_mistral': 1995, 'n_tokens_neox': 1903, 'n_words': 485}