Skip to content

Add tests for deprecations #833

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

Merged
merged 6 commits into from
Jul 8, 2025
Merged
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
12 changes: 1 addition & 11 deletions lib/git/path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@ module Git
class Path
attr_accessor :path

def initialize(path, check_path = nil, must_exist: nil)
unless check_path.nil?
Git::Deprecation.warn(
'The "check_path" argument is deprecated and ' \
'will be removed in a future version. Use "must_exist:" instead.'
)
end

# default is true
must_exist = must_exist.nil? && check_path.nil? ? true : must_exist || check_path

def initialize(path, must_exist: true)
path = File.expand_path(path)

raise ArgumentError, 'path does not exist', [path] if must_exist && !File.exist?(path)
Expand Down
11 changes: 1 addition & 10 deletions lib/git/stash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,7 @@
module Git
# A stash in a Git repository
class Stash
def initialize(base, message, existing = nil, save: nil)
unless existing.nil?
Git::Deprecation.warn(
'The "existing" argument is deprecated and will be removed in a future version. Use "save:" instead.'
)
end

# default is false
save = existing.nil? && save.nil? ? false : save | existing

def initialize(base, message, save: false)
@base = base
@message = message
self.save unless save
Expand Down
6 changes: 3 additions & 3 deletions tests/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
$stderr.sync = true

# Make tests that emit a deprecation warning fail

#
# Deprecation warnings should not be ignored.

#
# This is important so that:
# * when a user sees a deprecation warning, they can be confident it is coming from
# their code and not this gem
# * test output is clean and does not contain noisey deprecation warnings

#
# Tests whose purpose is to test that a deprecation warning is issued in the right
# circumstance should mock Git::Deprecation#warn to avoid raising an error.
#
Expand Down
82 changes: 82 additions & 0 deletions tests/units/test_log_deprecations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# frozen_string_literal: true

require 'test_helper'
require 'git'
require 'fileutils'
require 'tmpdir'

# A test case to verify the deprecation warnings for methods on the Git::Log class.
class LogDeprecationsTest < Test::Unit::TestCase
# Set up a temporary Git repository with a single commit before each test.
def setup
@repo_path = Dir.mktmpdir('git_test')
@repo = Git.init(@repo_path)

# Create a commit so the log has an entry to work with.
Dir.chdir(@repo_path) do
File.write('file.txt', 'content')
@repo.add('file.txt')
@repo.commit('Initial commit')
end

@log = @repo.log
@first_commit = @repo.gcommit('HEAD')
end

# Clean up the temporary repository after each test.
def teardown
FileUtils.rm_rf(@repo_path)
end

# Test the deprecation warning and functionality of Git::Log#each
def test_each_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#each is deprecated. Call #execute and then #each on the result object.'
)

commits = @log.map { |c| c }

assert_equal(1, commits.size, 'The #each method should still yield the correct number of commits.')
assert_equal(@first_commit.sha, commits.first.sha, 'The yielded commit should have the correct SHA.')
end

# Test the deprecation warning and functionality of Git::Log#size
def test_size_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#size is deprecated. Call #execute and then #size on the result object.'
)
assert_equal(1, @log.size, 'The #size method should still return the correct number of commits.')
end

# Test the deprecation warning and functionality of Git::Log#to_s
def test_to_s_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#to_s is deprecated. Call #execute and then #to_s on the result object.'
)
assert_equal(@first_commit.sha, @log.to_s, 'The #to_s method should return the commit SHA.')
end

# Test the deprecation warning and functionality of Git::Log#first
def test_first_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#first is deprecated. Call #execute and then #first on the result object.'
)
assert_equal(@first_commit.sha, @log.first.sha, 'The #first method should return the correct commit.')
end

# Test the deprecation warning and functionality of Git::Log#last
def test_last_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#last is deprecated. Call #execute and then #last on the result object.'
)
assert_equal(@first_commit.sha, @log.last.sha, 'The #last method should return the correct commit.')
end

# Test the deprecation warning and functionality of Git::Log#[]
def test_indexer_deprecation
Git::Deprecation.expects(:warn).with(
'Calling Git::Log#[] is deprecated. Call #execute and then #[] on the result object.'
)
assert_equal(@first_commit.sha, @log[0].sha, 'The #[] method should return the correct commit at the specified index.')
end
end
72 changes: 72 additions & 0 deletions tests/units/test_object_new.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require 'test_helper'
require 'git'
require 'fileutils'
require 'tmpdir'

# A test case to verify the functionality of the Git::Object.new factory method.
class ObjectNewTest < Test::Unit::TestCase
# Set up a temporary Git repository with objects of different types.
def setup
@repo_path = Dir.mktmpdir('git_test')
@repo = Git.init(@repo_path)

Dir.chdir(@repo_path) do
File.write('file.txt', 'This is a test file.')
@repo.add('file.txt')
@repo.commit('Initial commit')
@repo.add_tag('v1.0', message: 'Version 1.0', annotate: true)
end

@commit = @repo.gcommit('HEAD')
@tree = @commit.gtree
@blob = @tree.blobs['file.txt']
@tag = @repo.tag('v1.0')
end

# Clean up the temporary repository after each test.
def teardown
FileUtils.rm_rf(@repo_path)
end

# Test that the factory method creates a Git::Object::Commit for a commit SHA.
def test_new_creates_commit_object
object = Git::Object.new(@repo, @commit.sha)
assert_instance_of(Git::Object::Commit, object, 'Should create a Commit object.')
assert(object.commit?, 'Object#commit? should be true.')
end

# Test that the factory method creates a Git::Object::Tree for a tree SHA.
def test_new_creates_tree_object
object = Git::Object.new(@repo, @tree.sha)
assert_instance_of(Git::Object::Tree, object, 'Should create a Tree object.')
assert(object.tree?, 'Object#tree? should be true.')
end

# Test that the factory method creates a Git::Object::Blob for a blob SHA.
def test_new_creates_blob_object
object = Git::Object.new(@repo, @blob.sha)
assert_instance_of(Git::Object::Blob, object, 'Should create a Blob object.')
assert(object.blob?, 'Object#blob? should be true.')
end

# Test that using the deprecated `is_tag` argument creates a Tag object
# and issues a deprecation warning.
def test_new_with_is_tag_deprecation
# Set up the mock expectation for the deprecation warning.
Git::Deprecation.expects(:warn).with(
'Git::Object.new with is_tag argument is deprecated. Use Git::Object::Tag.new instead.'
)

# Action: Call the factory method with the deprecated argument.
# The `objectish` here is the tag name, as was the old pattern.
tag_object = Git::Object.new(@repo, 'v1.0', nil, true)

# Verification
assert_instance_of(Git::Object::Tag, tag_object, 'Should create a Tag object.')
assert(tag_object.tag?, 'Object#tag? should be true.')
assert_equal('v1.0', tag_object.name, 'Tag object should have the correct name.')
# Mocha automatically verifies the expectation at the end of the test.
end
end
167 changes: 167 additions & 0 deletions tests/units/test_set_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# frozen_string_literal: true

require 'test_helper'
require 'git'
require 'fileutils'
require 'tmpdir'

# A test case to demonstrate the use of Git::Base#set_index
#
# This test case will to programmatically create a new commit without affecting the
# main working directory or index.
#
class SetIndexTest < Test::Unit::TestCase
# Set up a temporary Git repository before each test.
def setup
# Create a temporary directory for the repository
@repo_path = Dir.mktmpdir('git_test')

# Initialize a new Git repository in the temporary directory
@repo = Git.init(@repo_path)

# Change into the repo directory to perform file operations
Dir.chdir(@repo_path) do
# Create and commit an initial file to establish a HEAD and a root tree.
# This gives us a base state to work from.
File.write('file1.txt', 'This is the first file.')
@repo.add('file1.txt')
@repo.commit('Initial commit')
end
end

attr_reader :repo_path, :repo

# Clean up the temporary repository after each test.
def teardown
FileUtils.rm_rf(repo_path)
end

# Tests that `set_index` can point to a new, non-existent index file
# when `must_exist: false` is specified.
def test_set_index_with_must_exist_false_for_new_path
custom_index_path = File.join(repo_path, 'custom_index')
assert(!File.exist?(custom_index_path), 'Precondition: Custom index file should not exist.')

# Action: Set the index to a new path, allowing it to not exist.
repo.set_index(custom_index_path, must_exist: false)

# Verification: The repo object should now point to the new index path.
assert_equal(custom_index_path, repo.index.path, 'Index path should be updated to the custom path.')
end

# Tests that `set_index` successfully points to an existing index file
# when `must_exist: true` is specified.
def test_set_index_with_must_exist_true_for_existing_path
original_index_path = repo.index.path
assert(File.exist?(original_index_path), 'Precondition: Original index file should exist.')

# Action: Set the index to the same, existing path, explicitly requiring it to exist.
repo.set_index(original_index_path, must_exist: true)

# Verification: The index path should remain unchanged.
assert_equal(original_index_path, repo.index.path, 'Index path should still be the original path.')
end

# Tests that `set_index` raises an ArgumentError when trying to point to a
# non-existent index file with the default behavior (`must_exist: true`).
def test_set_index_with_must_exist_true_raises_error_for_new_path
non_existent_path = File.join(repo_path, 'no_such_file')
assert(!File.exist?(non_existent_path), 'Precondition: The target index path should not exist.')

# Action & Verification: Assert that an ArgumentError is raised.
assert_raise(ArgumentError, 'Should raise ArgumentError for a non-existent index path.') do
repo.set_index(non_existent_path) # must_exist defaults to true
end
end

# Tests that using the deprecated `check` argument issues a warning via mocking.
def test_set_index_with_deprecated_check_argument
custom_index_path = File.join(repo_path, 'custom_index')
assert(!File.exist?(custom_index_path), 'Precondition: Custom index file should not exist.')

# Set up the mock expectation.
# We expect Git::Deprecation.warn to be called once with a message
# matching the expected deprecation warning.
Git::Deprecation.expects(:warn).with(
regexp_matches(/The "check" argument is deprecated/)
)

# Action: Use the deprecated positional argument `check = false`
repo.set_index(custom_index_path, false)

# Verification: The repo object should still point to the new index path.
assert_equal(custom_index_path, repo.index.path, 'Index path should be updated even with deprecated argument.')
# Mocha automatically verifies the expectation at the end of the test.
end

# This test demonstrates creating a new commit on a new branch by
# manipulating a custom, temporary index file. This allows for building
# commits programmatically without touching the working directory or the
# default .git/index.
def test_programmatic_commit_with_set_index
# 1. Get the initial commit object to use as a parent for our new commit.
main_commit = repo.gcommit('main')
assert(!main_commit.nil?, 'Initial commit should exist.')

# 2. Define a path for a new, temporary index file within the repo directory.
custom_index_path = File.join(repo_path, 'custom_index')
assert(!File.exist?(custom_index_path), 'Custom index file should not exist yet.')

# 3. Point the git object to use our custom index file.
# Since the file doesn't exist yet, we must pass `must_exist: false`.
repo.set_index(custom_index_path, must_exist: false)
assert_equal(custom_index_path, repo.index.path, 'The git object should now be using the custom index.')

# 4. Populate the new index by reading the tree from our initial commit into it.
# This stages all the files from the 'main' commit in our custom index.
repo.read_tree(main_commit.gtree.sha)

# 5. Programmatically create a new file blob and add it to our custom index.
# This simulates `git add` for a new file, but operates directly on the index.
new_content = 'This is a brand new file.'
blob_sha = Tempfile.create('new_blob_content') do |file|
file.write(new_content)
file.rewind
# Use `git hash-object -w` to write the blob to the object database and get its SHA
repo.lib.send(:command, 'hash-object', '-w', file.path)
end
repo.lib.send(:command, 'update-index', '--add', '--cacheinfo', "100644,#{blob_sha},new_file.txt")

# 6. Write the state of the custom index to a new tree object in the Git database.
new_tree_sha = repo.write_tree
assert_match(/^[0-9a-f]{40}$/, new_tree_sha, 'A new tree SHA should be created.')

# 7. Create a new commit object from the new tree.
# This commit will have the initial commit as its parent.
new_commit = repo.commit_tree(
new_tree_sha,
parents: [main_commit.sha],
message: 'Commit created programmatically via custom index'
)
assert(new_commit.commit?, 'A new commit object should be created.')

# 8. Create a new branch pointing to our new commit.
repo.branch('feature-branch').update_ref(new_commit)
assert(repo.branch('feature-branch').gcommit.sha == new_commit.sha, 'feature-branch should point to the new commit.')

# --- Verification ---
# Verify the history of the new branch
feature_log = repo.log.object('feature-branch').execute
main_commit_sha = repo.rev_parse('main') # Get SHA directly for reliable comparison

assert_equal(2, feature_log.size, 'Feature branch should have two commits.')
assert_equal(new_commit.sha, feature_log.first.sha, 'HEAD of feature-branch should be our new commit.')
assert_equal(main_commit_sha, feature_log.last.sha, 'Parent of new commit should be the initial commit.')

# Verify that the main branch is unchanged
main_log = repo.log.object('main').execute
assert_equal(1, main_log.size, 'Main branch should still have one commit.')
assert_equal(main_commit_sha, main_log.first.sha, 'Main branch should still point to the initial commit.')

# Verify the contents of the new commit's tree
new_commit_tree = new_commit.gtree
assert(new_commit_tree.blobs.key?('file1.txt'), 'Original file should exist in the new tree.')
assert(new_commit_tree.blobs.key?('new_file.txt'), 'New file should exist in the new tree.')
assert_equal(new_content, new_commit_tree.blobs['new_file.txt'].contents, 'Content of new file should match.')
end
end
Loading