Skip to content

Adding unit tests#15

Merged
KelCodesStuff merged 8 commits intomainfrom
adding-unit-tests
Sep 26, 2025
Merged

Adding unit tests#15
KelCodesStuff merged 8 commits intomainfrom
adding-unit-tests

Conversation

@KelCodesStuff
Copy link
Owner

Description of Change

Adding Unit Test target

Adding unit tests for:

  • MetalCommandTests
  • MetalDeviceTests
  • MetalErrorHandlingTests
  • MetalIntegrationTests
  • MetalResourceTests

Feedback Focus (Optional)

Please list here any items you want reviewers to pay particular attention to when reviewing

Code Review Checklist:

Please make sure to review and check the following items before requesting reviewers:

  • Have you done an initial review of the changes?
  • Have you updated README instructions to reflect any code/process changes? (Optional depending on what was changed)

@KelCodesStuff KelCodesStuff merged commit 75aead2 into main Sep 26, 2025
1 check passed
@gemini-code-assist
Copy link

Summary of Changes

Hello @KelCodesStuff, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the project's test coverage by introducing a new unit testing infrastructure for the Metal-Performance-Tester. It establishes a dedicated unit test target within the Xcode project and integrates a comprehensive set of Swift-based XCTest files. These tests are designed to validate the core functionalities and interactions of the Metal API components, ensuring the robustness and reliability of the application's Metal-related operations.

Highlights

  • New Unit Test Target: A dedicated 'Unit-Tests' target has been added to the Xcode project, enabling the execution of unit tests for the Metal-Performance-Tester application.
  • Comprehensive Metal API Testing: A suite of new unit tests has been introduced, covering various aspects of the Metal API, including command buffers, device capabilities, error handling, resource management (queues, libraries, textures, buffers), and overall integration.
  • Test Plan Configuration: The Unit-Tests.xctestplan has been configured to enable parallelizable and random execution ordering for tests, optimizing test run efficiency and helping detect order-dependent issues.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive suite of unit tests for the project's Metal components, which is an excellent step towards ensuring code quality and stability. The tests cover device initialization, resource creation, command execution, and error handling. My review provides feedback to enhance the robustness and maintainability of these new tests, including addressing a hardcoded development team ID, improving test logic to prevent brittleness, and increasing the clarity of test intentions.

buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = CT2P7VWHVS;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Hardcoding the DEVELOPMENT_TEAM in the project file can cause build failures for other contributors who are not part of this team. It's a best practice to avoid team-specific settings in shared project configurations to ensure the project can be built by any developer without modification.

 

Comment on lines +141 to +142
XCTAssertTrue(statusAfterCommit == .enqueued || statusAfterCommit == .committed || statusAfterCommit == .completed,
"Command buffer should be enqueued, committed, or completed after commit, got: \(statusAfterCommit) (rawValue: \(statusAfterCommit.rawValue))")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The assertion for the command buffer status after commit is missing the .scheduled case. A command buffer can transition to .scheduled after being committed and before completing. This omission makes the test brittle as it could fail unexpectedly depending on the execution speed and scheduling by the system.

Suggested change
XCTAssertTrue(statusAfterCommit == .enqueued || statusAfterCommit == .committed || statusAfterCommit == .completed,
"Command buffer should be enqueued, committed, or completed after commit, got: \(statusAfterCommit) (rawValue: \(statusAfterCommit.rawValue))")
let validStatuses: [MTLCommandBufferStatus] = [.enqueued, .committed, .scheduled, .completed]
XCTAssertTrue(validStatuses.contains(statusAfterCommit),
"Command buffer should be enqueued, committed, scheduled, or completed after commit, got: \(statusAfterCommit) (rawValue: \(statusAfterCommit.rawValue))")

"options" : {

"parallelizable" : true,
"randomExecutionOrdering" : false,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Setting randomExecutionOrdering to true is a recommended practice for unit tests. It helps uncover hidden dependencies between tests by running them in a different order on each run, leading to a more robust and reliable test suite.

        "randomExecutionOrdering" : true,

Comment on lines +38 to +49
let commandBuffer = commandQueue.makeCommandBuffer()
XCTAssertNotNil(commandBuffer, "Command buffer should be created")

if let commandBuffer = commandBuffer {
XCTAssertTrue(commandBuffer.device === device, "Command buffer device should match")

// Test command buffer execution
commandBuffer.commit()
commandBuffer.waitUntilCompleted()

XCTAssertEqual(commandBuffer.status, .completed, "Command buffer should complete")
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using if let to unwrap commandBuffer after already asserting it's not nil with XCTAssertNotNil introduces unnecessary nesting. Refactoring to use guard let at the beginning of the test makes the code flatter, more readable, and follows Swift's standard practice for handling optionals that are required for the subsequent code to execute.

Suggested change
let commandBuffer = commandQueue.makeCommandBuffer()
XCTAssertNotNil(commandBuffer, "Command buffer should be created")
if let commandBuffer = commandBuffer {
XCTAssertTrue(commandBuffer.device === device, "Command buffer device should match")
// Test command buffer execution
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
XCTAssertEqual(commandBuffer.status, .completed, "Command buffer should complete")
}
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
XCTFail("Command buffer should be created")
return
}
XCTAssertTrue(commandBuffer.device === device, "Command buffer device should match")
// Test command buffer execution
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
XCTAssertEqual(commandBuffer.status, .completed, "Command buffer should complete")

// Test device capabilities - check for any GPU family support
// Note: AMD GPUs don't support Apple's proprietary GPU families
// Instead, we'll check if the device can create basic Metal resources
let supportsAppleGPU = device.supportsFamily(.apple1) || device.supportsFamily(.apple2) || device.supportsFamily(.apple3) || device.supportsFamily(.apple4)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The list of supported GPU families is incomplete. It only checks up to .apple4, but newer families like .apple5, .apple6, etc., already exist. This makes the test brittle, as it may fail on newer hardware. Consider if this check provides significant value, or if testMetalDeviceCapabilities is sufficient to verify a working device.

Comment on lines +31 to +48
func testMetalErrorHandling() throws {
// Test Metal error handling
XCTAssertNotNil(device, "Device should be available")

// Test that invalid operations don't crash
// Note: We avoid creating invalid descriptors that would cause crashes
// Instead, we test that the API handles edge cases gracefully

// Test with minimal valid descriptor
let validDescriptor = MTLTextureDescriptor()
validDescriptor.width = 1
validDescriptor.height = 1
validDescriptor.pixelFormat = .bgra8Unorm
validDescriptor.usage = .renderTarget

let texture = device.makeTexture(descriptor: validDescriptor)
XCTAssertNotNil(texture, "Valid descriptor should create texture")
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The name and documentation for testMetalErrorHandling are misleading. The test claims to verify error handling but only executes a valid code path (creating a texture with a valid descriptor). A proper error handling test should attempt an operation that is expected to fail and assert that the failure is handled gracefully (e.g., by returning nil or throwing an error) rather than crashing.

Comment on lines +52 to +59
func testMetalDeviceLimits() throws {
// Test Metal device limits
XCTAssertNotNil(device, "Device should be available")

// Test maximum buffer length
let maxBufferLength = device.maxBufferLength
XCTAssertGreaterThan(maxBufferLength, 0, "Max buffer length should be positive")
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test, testMetalDeviceLimits, is a duplicate of a test in MetalDeviceTests.swift. Duplicating tests increases maintenance overhead and can lead to inconsistencies. It's best to have a single, authoritative test for each specific behavior.

Comment on lines +137 to +168
func testMetalMultipleWorkflows() throws {
// Test multiple concurrent Metal workflows
XCTAssertNotNil(device, "Device should be available")

// Create multiple command queues
let commandQueue1 = device.makeCommandQueue()
let commandQueue2 = device.makeCommandQueue()

XCTAssertNotNil(commandQueue1, "First command queue should be created")
XCTAssertNotNil(commandQueue2, "Second command queue should be created")

// Create command buffers from both queues
let commandBuffer1 = commandQueue1?.makeCommandBuffer()
let commandBuffer2 = commandQueue2?.makeCommandBuffer()

XCTAssertNotNil(commandBuffer1, "First command buffer should be created")
XCTAssertNotNil(commandBuffer2, "Second command buffer should be created")

// Execute both command buffers
if let commandBuffer1 = commandBuffer1 {
commandBuffer1.commit()
commandBuffer1.waitUntilCompleted()
}

if let commandBuffer2 = commandBuffer2 {
commandBuffer2.commit()
commandBuffer2.waitUntilCompleted()
}

// Both should complete successfully
XCTAssertTrue(true, "Multiple workflows should execute successfully")
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test testMetalMultipleWorkflows has a couple of issues:

  1. It claims to test concurrent workflows, but it waits for each command buffer to complete sequentially. This does not test concurrent execution.
  2. It ends with XCTAssertTrue(true, ...) which is a 'test smell'. The test should have meaningful assertions that verify the expected outcome, such as checking that the status of both command buffers is .completed.

To properly test concurrency, you could commit both command buffers first and then wait for both to complete, for example by using a DispatchGroup.

@KelCodesStuff KelCodesStuff deleted the adding-unit-tests branch September 29, 2025 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant