MPVKit publishes binaries for every commit on its main branch now, so a
version tag is no longer the only resolvable thing. Pinning the revision
means a fork fix is consumable as soon as its binaries land, with no release
ceremony there and no version bump here.
Pinned at 0097fea015fe, which carries the PiP subtitle fix: the iPadOS
Picture-in-Picture window no longer resizes when SRT subtitles appear and
disappear. Nine pin sites move together -- three project.pbxproj
requirements plus the six SwiftPM locks that check_apple_spm_locks.py keeps
in agreement.
tvos/scripts/wire_mpv.rb no longer hardcodes the version it writes; it reads
the revision out of the tvOS project's committed lock, so re-wiring the
tvOS target cannot silently revert the pin. Its test asserts the shape and
that all nine sites name one commit, rather than a literal sha, so moving
the pin stays one command:
scripts/set_mpvkit_revision.sh <40-char sha>
close #2078
155 lines
5.7 KiB
Ruby
Executable File
155 lines
5.7 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
require 'fileutils'
|
|
require 'json'
|
|
require 'minitest/autorun'
|
|
require 'open3'
|
|
require 'rbconfig'
|
|
require 'tmpdir'
|
|
require 'xcodeproj'
|
|
|
|
class WireMpvTest < Minitest::Test
|
|
SOURCE_NAMES = %w[
|
|
MpvPlayerCoreBase.swift
|
|
MpvPlayerPluginShared.swift
|
|
MpvPlayerCore.swift
|
|
MpvPlayerPlugin.swift
|
|
MpvPipController.swift
|
|
MpvAudioPlayerCore.swift
|
|
MpvAudioPlayerPlugin.swift
|
|
].freeze
|
|
AFFECTED_NAMES = %w[
|
|
MpvAudioPlayerCore.swift
|
|
MpvAudioPlayerPlugin.swift
|
|
].freeze
|
|
MPVKIT_LOCATION = 'https://github.com/edde746/MPVKit'
|
|
MPVKIT_REVISION = /\A[0-9a-f]{40}\z/.freeze
|
|
|
|
def setup
|
|
@temporary_root = Dir.mktmpdir('wire-mpv-test')
|
|
@tvos_root = File.join(@temporary_root, 'tvos')
|
|
FileUtils.mkdir_p(File.join(@tvos_root, 'scripts'))
|
|
FileUtils.cp_r(File.expand_path('../Runner.xcodeproj', __dir__), @tvos_root)
|
|
FileUtils.cp(File.expand_path('wire_mpv.rb', __dir__), File.join(@tvos_root, 'scripts'))
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.remove_entry(@temporary_root)
|
|
end
|
|
|
|
def test_restores_missing_references_and_is_idempotent
|
|
edit_project do |_project, _runner, group|
|
|
AFFECTED_NAMES.each do |name|
|
|
group.files.find { |file| file.display_name == name }&.remove_from_project
|
|
end
|
|
end
|
|
|
|
run_wire_mpv
|
|
run_wire_mpv
|
|
assert_complete_source_graph
|
|
end
|
|
|
|
def test_restores_membership_when_references_remain
|
|
edit_project do |_project, runner, group|
|
|
affected = group.files.select { |file| AFFECTED_NAMES.include?(file.display_name) }
|
|
runner.source_build_phase.files.each do |build_file|
|
|
build_file.remove_from_project if affected.include?(build_file.file_ref)
|
|
end
|
|
end
|
|
|
|
run_wire_mpv
|
|
assert_complete_source_graph
|
|
end
|
|
|
|
def test_restores_missing_package_product_and_framework_edges
|
|
edit_project do |_project, runner, _group|
|
|
runner.package_product_dependencies
|
|
.select { |product| product.product_name == 'MPVKit' }
|
|
.each(&:remove_from_project)
|
|
end
|
|
|
|
run_wire_mpv
|
|
assert_complete_source_graph
|
|
end
|
|
|
|
# MPVKit is pinned by commit so any upstream commit is consumable without a
|
|
# release. Assert the shape and that every pin site agrees, rather than a
|
|
# literal sha: scripts/set_mpvkit_revision.sh is the only thing that writes
|
|
# one, and it must stay the only file to edit when the pin moves.
|
|
def test_all_apple_targets_pin_mpvkit_to_one_commit
|
|
repository_root = File.expand_path('../..', __dir__)
|
|
revisions = {}
|
|
|
|
%w[ios macos tvos].each do |platform|
|
|
lock_paths = [
|
|
File.join(repository_root, platform, 'Runner.xcworkspace', 'xcshareddata', 'swiftpm', 'Package.resolved'),
|
|
File.join(
|
|
repository_root, platform, 'Runner.xcodeproj', 'project.xcworkspace',
|
|
'xcshareddata', 'swiftpm', 'Package.resolved'
|
|
),
|
|
]
|
|
lock_paths.each do |resolved_path|
|
|
resolved = JSON.parse(File.read(resolved_path))
|
|
pin = resolved.fetch('pins').find { |candidate| candidate.fetch('identity') == 'mpvkit' }
|
|
refute_nil pin, "#{resolved_path} must resolve MPVKit"
|
|
assert_equal MPVKIT_LOCATION, pin['location'], "#{resolved_path} MPVKit source"
|
|
state = pin.fetch('state')
|
|
assert_match MPVKIT_REVISION, state['revision'].to_s, "#{resolved_path} MPVKit revision"
|
|
refute state.key?('version'), "#{resolved_path} pins MPVKit by version; it must pin a commit"
|
|
refute state.key?('branch'), "#{resolved_path} pins MPVKit by branch; it must pin a commit"
|
|
revisions[resolved_path] = state['revision']
|
|
end
|
|
|
|
project = Xcodeproj::Project.open(File.join(repository_root, platform, 'Runner.xcodeproj'))
|
|
package = project.root_object.package_references.find do |candidate|
|
|
(candidate.repositoryURL rescue nil) == MPVKIT_LOCATION
|
|
end
|
|
refute_nil package, "#{platform} must reference MPVKit"
|
|
requirement = package.requirement
|
|
assert_equal 'revision', requirement['kind'], "#{platform} MPVKit requirement kind"
|
|
assert_match MPVKIT_REVISION, requirement['revision'].to_s, "#{platform} MPVKit requirement revision"
|
|
revisions[File.join(platform, 'Runner.xcodeproj')] = requirement['revision']
|
|
end
|
|
|
|
assert_equal 1, revisions.values.uniq.count, "Apple targets disagree on the MPVKit commit: #{revisions}"
|
|
end
|
|
|
|
private
|
|
|
|
def project_path
|
|
File.join(@tvos_root, 'Runner.xcodeproj')
|
|
end
|
|
|
|
def edit_project
|
|
project = Xcodeproj::Project.open(project_path)
|
|
runner = project.targets.find { |target| target.name == 'Runner' }
|
|
group = project.main_group['Runner']['MpvPlayer']
|
|
yield project, runner, group
|
|
project.save
|
|
end
|
|
|
|
def run_wire_mpv
|
|
script = File.join(@tvos_root, 'scripts', 'wire_mpv.rb')
|
|
output, status = Open3.capture2e(RbConfig.ruby, script)
|
|
assert status.success?, output
|
|
end
|
|
|
|
def assert_complete_source_graph
|
|
project = Xcodeproj::Project.open(project_path)
|
|
runner = project.targets.find { |target| target.name == 'Runner' }
|
|
group = project.main_group['Runner']['MpvPlayer']
|
|
|
|
SOURCE_NAMES.each do |name|
|
|
references = group.files.select { |file| file.display_name == name }
|
|
assert_equal 1, references.count, "expected one reference for #{name}"
|
|
memberships = runner.source_build_phase.files_references.count { |file| file == references.first }
|
|
assert_equal 1, memberships, "expected one Runner source membership for #{name}"
|
|
end
|
|
|
|
products = runner.package_product_dependencies.select { |product| product.product_name == 'MPVKit' }
|
|
assert_equal 1, products.count, 'expected one MPVKit product dependency'
|
|
framework_links = runner.frameworks_build_phase.files.count { |file| file.product_ref == products.first }
|
|
assert_equal 1, framework_links, 'expected one MPVKit framework link'
|
|
end
|
|
end
|