Skip to content

quickpass update, not complete #18

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/Packages
/*.xcodeproj
Package.resolved
.swiftpm
23 changes: 12 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// swift-tools-version:5.10
// swift-tools-version:6.0

/*
This source file is part of the Swift.org open source project

Copyright 2015 – 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
//===----------------------------------------------------------------------===//
//
// This source file is part of the example package dealer open source project
//
// Copyright (c) 2015-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import PackageDescription

Expand All @@ -27,7 +28,7 @@ let package = Package(
from: "3.0.0"),
.package(
url: "https://github.com/apple/swift-argument-parser.git",
from: "0.4.4"),
from: "1.6.0"),
],
targets: [
.executableTarget(
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ To build this example package:

git clone https://github.com/apple/example-package-dealer.git
cd example-package-dealer
swift run
swift run dealer <count>

For more information, visit [Swift's package manager documentation](https://www.swift.org/package-manager/).
103 changes: 55 additions & 48 deletions Sources/dealer/Dealer.swift
Original file line number Diff line number Diff line change
@@ -1,67 +1,74 @@
/*
This source file is part of the Swift.org open source project
//===----------------------------------------------------------------------===//
//
// This source file is part of the example package dealer open source project
//
// Copyright (c) 2015-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

Copyright 2015 – 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import ArgumentParser
import DeckOfPlayingCards
import Foundation
import PlayingCard

#if os(Linux)
import Glibc
import Glibc
#endif

import Foundation
import DeckOfPlayingCards
import PlayingCard
import ArgumentParser

@main
struct Dealer: ParsableCommand {
enum Error: Swift.Error, CustomStringConvertible {
case notEnoughCards
enum Error: Swift.Error, CustomStringConvertible {
case notEnoughCards

var description: String {
switch self {
case .notEnoughCards:
return "Not enough cards"
}
}
var description: String {
switch self {
case .notEnoughCards:
return "Not enough cards"
}
}
}

static var configuration = CommandConfiguration(
abstract: "Shuffles a deck of playing cards and deals a number of cards.",
discussion: """
For each count argument, prints a line of tab-delimited cards to stdout,
or if there aren't enough cards remaining,
prints "Not enough cards" to stderr and exits with a nonzero status.
""")
static let configuration = CommandConfiguration(
abstract: "Shuffles a deck of playing cards and deals a number of cards.",
discussion: """
For each count argument, prints a line of tab-delimited cards to stdout,
or if there aren't enough cards remaining,
prints "Not enough cards" to stderr and exits with a nonzero status.
""")

@Argument(help: .init("The number of cards to deal at a time.",
valueName: "count"))
var counts: [UInt]
@Argument(
help: .init(
"The number of cards to deal at a time.",
valueName: "count"))
var counts: [UInt]

mutating func run() throws {
#if os(Linux)
srandom(UInt32(clock()))
#endif
mutating func run() throws {
#if os(Linux)
srandom(UInt32(clock()))
#endif

var deck = Deck.standard52CardDeck()
deck.shuffle()
var deck = Deck.standard52CardDeck()
deck.shuffle()

for count in counts {
var cards: [PlayingCard] = []
for count in counts {
var cards: [PlayingCard] = []

for _ in 0..<count {
guard let card = deck.deal() else {
Self.exit(withError: Error.notEnoughCards)
}
for _ in 0..<count {
guard let card = deck.deal() else {
Self.exit(withError: Error.notEnoughCards)
}

cards.append(card)
}
cards.append(card)
}

print(cards.map(\.description).joined(separator: "\t"))
}
print(cards.map(\.description).joined(separator: "\t"))
}
}
}

// Empty class that provides a convenient handle to request
// the bundle that contains this code.
class BundleMarker {}
169 changes: 92 additions & 77 deletions Tests/DealerTests/DealerTests.swift
Original file line number Diff line number Diff line change
@@ -1,103 +1,118 @@
/*
This source file is part of the Swift.org open source project
//===----------------------------------------------------------------------===//
//
// This source file is part of the example package dealer open source project
//
// Copyright (c) 2021-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import Foundation
import Testing

Copyright 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
import class Foundation.Bundle

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
@testable import dealer

import XCTest
import class Foundation.Bundle
struct DealerTests {
@Test
func testUsage() throws {
let (status, output, error) = try execute(with: ["--help"])
#expect(status == EXIT_SUCCESS)
#expect(
output?.starts(
with: "OVERVIEW: Shuffles a deck of playing cards and deals a number of cards.") ?? false)
#expect(error?.isEmpty == true)
}

final class DealerTests: XCTestCase {
func testUsage() throws {
let (status, output, error) = try execute(with: ["--help"])
XCTAssertEqual(status, EXIT_SUCCESS)
XCTAssert(output?.starts(with: "OVERVIEW: Shuffles a deck of playing cards and deals a number of cards.") ?? false)
XCTAssertEqual(error, "")
}
@Test
func testDealOneCard() throws {
let (status, output, error) = try execute(with: ["1"])
#expect(status == EXIT_SUCCESS)
#expect(output?.filter(\.isPlayingCardSuit).count == 1)

func testDealOneCard() throws {
let (status, output, error) = try execute(with: ["1"])
XCTAssertEqual(status, EXIT_SUCCESS)
XCTAssertEqual(output?.filter(\.isPlayingCardSuit).count, 1)
#expect(error?.isEmpty == true)

XCTAssertEqual(error, "")
}
}

func testDealTenCards() throws {
let (status, output, error) = try execute(with: ["10"])
XCTAssertEqual(status, EXIT_SUCCESS)
XCTAssertEqual(output?.filter(\.isPlayingCardSuit).count, 10)
@Test
func testDealTenCards() throws {
let (status, output, error) = try execute(with: ["10"])
#expect(status == EXIT_SUCCESS)
#expect(output?.filter(\.isPlayingCardSuit).count == 10)

XCTAssertEqual(error, "")
}
#expect(error?.isEmpty == true)

func testDealThirteenCardsFourTimes() throws {
let (status, output, error) = try execute(with: ["13", "13", "13", "13"])
XCTAssertEqual(status, EXIT_SUCCESS)
XCTAssertEqual(output?.filter(\.isPlayingCardSuit).count, 52)
XCTAssertEqual(output?.filter(\.isNewline).count, 4)
}

XCTAssertEqual(error, "")
}
@Test
func testDealThirteenCardsFourTimes() throws {
let (status, output, error) = try execute(with: ["13", "13", "13", "13"])
#expect(status == EXIT_SUCCESS)
#expect(output?.filter(\.isPlayingCardSuit).count == 52)
#expect(output?.filter(\.isNewline).count == 4)

func testDealOneHundredCards() throws {
let (status, output, error) = try execute(with: ["100"])
XCTAssertNotEqual(status, EXIT_SUCCESS)
XCTAssertEqual(output, "")
XCTAssertEqual(error, "Error: Not enough cards\n")
}
#expect(error?.isEmpty == true)

/// Returns path to the built products directory.
var productsDirectory: URL {
#if os(macOS)
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
return bundle.bundleURL.deletingLastPathComponent()
}
fatalError("couldn't find the products directory")
#else
return Bundle.main.bundleURL
#endif
}
}

private func execute(with arguments: [String] = []) throws -> (status: Int32, output: String?, error: String?) {
let process = Process()
process.executableURL = productsDirectory.appendingPathComponent("dealer")
process.arguments = arguments
@Test
func testDealOneHundredCards() throws {
let (status, output, error) = try execute(with: ["100"])
#expect(status != EXIT_SUCCESS)
#expect(output?.isEmpty == true)

let outputPipe = Pipe()
process.standardOutput = outputPipe
#expect(error == "Error: Not enough cards\n")
}

let errorPipe = Pipe()
process.standardError = errorPipe
/// Returns path to the built products directory.
var productsDirectory: URL {
#if os(macOS)
return Bundle(for: BundleMarker.self).bundleURL.deletingLastPathComponent()
#else
return Bundle.main.bundleURL
#endif
}

try process.run()
process.waitUntilExit()
private func execute(with arguments: [String] = []) throws -> (
status: Int32, output: String?, error: String?
) {
let process = Process()
process.executableURL = productsDirectory.appendingPathComponent("dealer")
process.arguments = arguments

let status = process.terminationStatus
let outputPipe = Pipe()
process.standardOutput = outputPipe

let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: outputData, encoding: .utf8)
let errorPipe = Pipe()
process.standardError = errorPipe

let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let error = String(data: errorData, encoding: .utf8)
try process.run()
process.waitUntilExit()

return (status, output, error)
}
let status = process.terminationStatus

let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: outputData, encoding: .utf8)

let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let error = String(data: errorData, encoding: .utf8)

return (status, output, error)
}
}

// MARK: -

private extension Character {
var isPlayingCardSuit: Bool {
switch self {
case "♠︎", "♡", "♢", "♣︎":
return true
default:
return false
}
extension Character {
fileprivate var isPlayingCardSuit: Bool {
switch self {
case "♠︎", "♡", "♢", "♣︎":
return true
default:
return false
}
}
}