Skip to content

Commit

Permalink
Move LocalPodspecPatch to dedicated file (#34025)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #34025

This diff moves the monkeypatch LocalPodspecPatch to a dedicated ruby file.
It also adds test for that

## Changelog
[iOS][Changed] - Move LocalPodspecPatch to dedicated file

Reviewed By: cortinico

Differential Revision: D37069361

fbshipit-source-id: 28fddb197484f45aa20ccac516c874e79448e999
  • Loading branch information
Riccardo Cipolleschi authored and facebook-github-bot committed Jun 27, 2022
1 parent b6bbbf8 commit 8fe2b59
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 44 deletions.
171 changes: 171 additions & 0 deletions scripts/cocoapods/__tests__/local_podspec_patch-test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

require "test/unit"
require "json"
require_relative "../local_podspec_patch.rb"
require_relative "./test_utils/FileMock.rb"
require_relative "./test_utils/DirMock.rb"
require_relative "./test_utils/PodMock.rb"
require_relative "./test_utils/LocalPodspecPatchMock.rb"

class LocalPodspecPatchTests < Test::Unit::TestCase
def setup
File.enable_testing_mode!
Dir.enable_testing_mode!
end

def teardown
File.reset()
Dir.reset()
end

# =================== #
# Test - Pods To Update #
# =================== #

def test_podsToUpdate_whenNoFilesExists_returnLocalPodspecs
# Arrange
react_native_path = "../node_modules/react-native"
globs = ["a/path/to/boost.podspec", "a/path/to/DoubleConversion.podspec"]
mocked_pwd = "a/path/to"
Dir.mocked_existing_globs(globs, "#{react_native_path}/third-party-podspecs/*")
Dir.set_pwd(mocked_pwd)

# Act
local_podspec = LocalPodspecPatch.pods_to_update(:react_native_path => react_native_path)

# Assert
assert_equal(local_podspec, [])
assert_equal(Dir.glob_invocation, ["#{react_native_path}/third-party-podspecs/*"])
assert_equal(File.exist_invocation_params, [
File.join(mocked_pwd, "Pods/Local Podspecs", "boost.podspec.json"),
File.join(mocked_pwd, "Pods/Local Podspecs", "DoubleConversion.podspec.json"),
])
end

def test_podsToUpdate_whenFilesExistsWithSameVersions_returnsEmpty
# Arrange
react_native_path = "../node_modules/react-native"
globs = ["a/path/to/boost.podspec", "a/path/to/DoubleConversion.podspec"]
mocked_pwd = "a/path/to"
prepare_PodsToUpdate_test_withMatchingVersions(react_native_path, globs, mocked_pwd)

# Act
local_podspec = LocalPodspecPatch.pods_to_update(:react_native_path => react_native_path)

# Assert
assert_equal(local_podspec, [])
assert_equal(Dir.glob_invocation, ["#{react_native_path}/third-party-podspecs/*"])
assert_equal(File.exist_invocation_params, [
File.join(mocked_pwd, "Pods/Local Podspecs", "boost.podspec.json"),
File.join(mocked_pwd, "Pods/Local Podspecs", "DoubleConversion.podspec.json"),
])
end

def test_podsToUpdate_whenFilesExistsWithDifferentVersions_returnsThem
# Arrange
react_native_path = "../node_modules/react-native"
globs = ["a/path/to/boost.podspec", "a/path/to/DoubleConversion.podspec"]
mocked_pwd = "a/path/to"
prepare_PodsToUpdate_test_withDifferentVersions(react_native_path, globs, mocked_pwd)

# Act
local_podspec = LocalPodspecPatch.pods_to_update(:react_native_path => react_native_path)

# Assert
assert_equal(local_podspec, [
"boost",
"DoubleConversion"
])
assert_equal(Dir.glob_invocation, ["#{react_native_path}/third-party-podspecs/*"])
assert_equal(File.exist_invocation_params, [
File.join(mocked_pwd, "Pods/Local Podspecs", "boost.podspec.json"),
File.join(mocked_pwd, "Pods/Local Podspecs", "DoubleConversion.podspec.json"),
])
end

# ======================================== #
# Test - Patch Detect Changes With Podfile #
# ======================================== #
def test_patchDetectChangesWithPodfile_whenAlreadyChanged_returnSameChangeSet()
local_pods = [
"boost",
"DoubleConversion"
]
LocalPodspecPatch.mock_local_podspecs(local_pods)
changes = {
:unchanged => ["some_pod"],
:changed => ["boost", "DoubleConversion", "another_pod"]
}

Pod::Lockfile.prepend(LocalPodspecPatch)

new_changes = Pod::Lockfile.new().patch_detect_changes_with_podfile(changes)

assert_equal(new_changes, {
:unchanged => ["some_pod"],
:changed => ["boost", "DoubleConversion", "another_pod"]
})
end

def test_patchDetectChangesWithPodfile_whenLocalPodsUnchanged_movesLocalPodsToChangeSet()
pods = [
"boost",
"DoubleConversion"
]
LocalPodspecPatch.mock_local_podspecs(pods)
changes = {
:unchanged => ["first_pod", "boost", "DoubleConversion"],
:changed => ["another_pod"]
}

Pod::Lockfile.prepend(LocalPodspecPatch)

new_changes = Pod::Lockfile.new().patch_detect_changes_with_podfile(changes)

assert_equal(new_changes, {
:unchanged => ["first_pod"],
:changed => ["another_pod", "boost", "DoubleConversion"]
})
end

# ========= #
# Utilities #
# ========= #
def prepare_PodsToUpdate_test_withMatchingVersions(react_native_path, globs, mocked_pwd)
File.mocked_existing_files([
"a/path/to/Pods/Local Podspecs/boost.podspec.json",
"a/path/to/Pods/Local Podspecs/DoubleConversion.podspec.json"
])
File.files_to_read({
"a/path/to/Pods/Local Podspecs/boost.podspec.json" => "{ \"version\": \"0.0.1\"}",
"a/path/to/Pods/Local Podspecs/DoubleConversion.podspec.json" => "{ \"version\": \"1.0.1\"}",
})
Dir.mocked_existing_globs(globs, "#{react_native_path}/third-party-podspecs/*")
Dir.set_pwd(mocked_pwd)
Pod::Specification.specs_from_file({
"../node_modules/react-native/third-party-podspecs/boost.podspec" => Pod::PodSpecMock.new(:version => "0.0.1"),
"../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" => Pod::PodSpecMock.new(:version => "1.0.1"),
})
end

def prepare_PodsToUpdate_test_withDifferentVersions(react_native_path, globs, mocked_pwd)
File.mocked_existing_files([
"a/path/to/Pods/Local Podspecs/boost.podspec.json",
"a/path/to/Pods/Local Podspecs/DoubleConversion.podspec.json"
])
File.files_to_read({
"a/path/to/Pods/Local Podspecs/boost.podspec.json" => "{ \"version\": \"0.0.1\"}",
"a/path/to/Pods/Local Podspecs/DoubleConversion.podspec.json" => "{ \"version\": \"1.0.1\"}",
})
Dir.mocked_existing_globs(globs, "#{react_native_path}/third-party-podspecs/*")
Dir.set_pwd(mocked_pwd)
Pod::Specification.specs_from_file({
"../node_modules/react-native/third-party-podspecs/boost.podspec" => Pod::PodSpecMock.new(:version => "0.1.1"),
"../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" => Pod::PodSpecMock.new(:version => "1.1.1"),
})
end
end
33 changes: 33 additions & 0 deletions scripts/cocoapods/__tests__/test_utils/DirMock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class Dir
@@exist_invocation_params = []
@@mocked_existing_dirs = []

@@glob_invocation = []
@@mocked_existing_globs = {}

@@pwd = nil

# Monkey patched exists? method.
# It is used also by the test runner, so it can't start monkey patched
# To use this, invoke the `is_testing` method before starting your test.
Expand All @@ -33,15 +38,43 @@ def self.mocked_existing_dirs(dirs)
@@mocked_existing_dirs = dirs
end

# Set what the `glob` function should return
def self.mocked_existing_globs(globs, path)
@@mocked_existing_globs[path] = globs
end

def self.glob_invocation
return @@glob_invocation
end

def self.glob(path)
@@glob_invocation.push(path)
return @@mocked_existing_globs[path]
end

def self.set_pwd(pwd)
@@pwd = pwd
end

def self.pwd
if @@pwd != nil
return @@pwd
end
return pwd
end

# Turn on the mocking features of the File mock
def self.enable_testing_mode!()
@@is_testing = true
end

# Resets all the settings for the File mock
def self.reset()
@@pwd = nil
@@mocked_existing_dirs = []
@@is_testing = false
@@exist_invocation_params = []
@@glob_invocation = []
@@mocked_existing_globs = {}
end
end
17 changes: 17 additions & 0 deletions scripts/cocoapods/__tests__/test_utils/FileMock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class File
@@open_invocation_count = 0

@@open_files = []

@@files_to_read = {}
attr_reader :collected_write
attr_reader :fsync_invocation_count

Expand Down Expand Up @@ -91,6 +93,10 @@ def self.open_files
return @@open_files
end

def self.file_invocation_params
return @@file_invocation_params
end

def write(text)
@collected_write.push(text.to_s)
end
Expand All @@ -99,6 +105,15 @@ def fsync()
@fsync_invocation_count += 1
end


def self.files_to_read(files)
@@files_to_read = files
end

def self.read(filepath)
return @@files_to_read[filepath]
end

# Resets all the settings for the File mock
def self.reset()
@@delete_invocation_count = 0
Expand All @@ -108,7 +123,9 @@ def self.reset()
@@open_invocation_count = 0
@@mocked_existing_files = []
@@is_testing = false
@@file_invocation_params = []
@@exist_invocation_params = []
@@files_to_read = {}
end


Expand Down
14 changes: 14 additions & 0 deletions scripts/cocoapods/__tests__/test_utils/LocalPodspecPatchMock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

module LocalPodspecPatch
def self.mock_local_podspecs(pods)
@@local_podspecs = pods
end

def reset()
@@local_podspecs = []
end
end
29 changes: 29 additions & 0 deletions scripts/cocoapods/__tests__/test_utils/PodMock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,33 @@ def self.reset()
@@executed_commands = []
end
end

class Specification
@@specs_from_file = {}

def self.specs_from_file(specs)
@@specs_from_file = specs
end

def self.from_file(path)
return @@specs_from_file[path]
end

def reset()
@@specs_from_file = {}
end
end

class PodSpecMock
attr_reader :version

def initialize(version: "0.0.1")
@version = version
end
end

class Lockfile
def initialize()
end
end
end
51 changes: 51 additions & 0 deletions scripts/cocoapods/local_podspec_patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

# Monkeypatch of `Pod::Lockfile` to ensure automatic update of dependencies integrated with a local podspec when their version changed.
# This is necessary because local podspec dependencies must be otherwise manually updated.
module LocalPodspecPatch
# Returns local podspecs whose versions differ from the one in the `react-native` package.
def self.pods_to_update(react_native_path: "../node_modules/react-native")
@@local_podspecs = Dir.glob("#{react_native_path}/third-party-podspecs/*").map { |file| File.basename(file, ".podspec") }
@@local_podspecs = @@local_podspecs.select do |podspec_name|

# Read local podspec to determine the cached version
local_podspec_path = File.join(
Dir.pwd, "Pods/Local Podspecs/#{podspec_name}.podspec.json"
)

# Local podspec cannot be outdated if it does not exist, yet
next unless File.exist?(local_podspec_path)

local_podspec = File.read(local_podspec_path)
local_podspec_json = JSON.parse(local_podspec)
local_version = local_podspec_json["version"]

# Read the version from a podspec from the `react-native` package
podspec_path = "#{react_native_path}/third-party-podspecs/#{podspec_name}.podspec"
current_podspec = Pod::Specification.from_file(podspec_path)
current_version = current_podspec.version.to_s
current_version != local_version
end
@@local_podspecs
end

# Patched `detect_changes_with_podfile` method
def detect_changes_with_podfile(podfile)
Pod::UI.puts "Invoke detect_changes_with_podfile patched method".red
changes = super(podfile)
return patch_detect_changes_with_podfile(changes)
end

def patch_detect_changes_with_podfile(changes)
@@local_podspecs.each do |local_podspec|
next unless changes[:unchanged].include?(local_podspec)

changes[:unchanged].delete(local_podspec)
changes[:changed] << local_podspec
end
changes
end
end
Loading

0 comments on commit 8fe2b59

Please sign in to comment.