diff --git a/.rubocop.yml b/.rubocop.yml index 45dac2b..e6d8a3e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,3 +5,21 @@ require: AllCops: TargetRubyVersion: 2.7 NewCops: enable + +Metrics/AbcSize: + Max: 21 + +Metrics/CyclomaticComplexity: + Max: 10 + +Metrics/MethodLength: + Max: 15 + +Metrics/PerceivedComplexity: + Max: 10 + +RSpec/MultipleMemoizedHelpers: + Max: 6 + +RSpec/NestedGroups: + Max: 4 diff --git a/lib/periphery/runner.rb b/lib/periphery/runner.rb index 59d6d21..42165be 100644 --- a/lib/periphery/runner.rb +++ b/lib/periphery/runner.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'open3' +require 'rubygems/version' module Periphery class Runner # :nodoc: @@ -23,10 +24,26 @@ def scan_arguments(options) next unless value value = nil if value.is_a?(TrueClass) - value = value.join(',') if value.is_a?(Array) + if value.is_a?(Array) + if Gem::Version.new(version) >= Gem::Version.new('2.18.0') + new_options << "--#{key.to_s.tr('_', '-')}" + new_options.push(*value.map(&:to_s)) + next + else + value = value.join(',') + end + end new_options << "--#{key.to_s.tr('_', '-')}" new_options << value&.to_s if value end end + + def version + arguments = [binary_path, 'version'] + stdout, stderr, status = Open3.capture3(*arguments) + raise "error: #{arguments} existed with status code #{status.exitstatus}. #{stderr}" unless status.success? + + stdout.strip + end end end diff --git a/spec/danger/danger_periphery_integration_spec.rb b/spec/danger/danger_periphery_integration_spec.rb index 5aa0378..6453651 100644 --- a/spec/danger/danger_periphery_integration_spec.rb +++ b/spec/danger/danger_periphery_integration_spec.rb @@ -14,12 +14,13 @@ let(:periphery_options) do { project: fixture('test.xcodeproj'), - targets: 'test', + targets: targets, schemes: 'test', skip_build: true, index_store_path: index_store_path } end + let(:targets) { 'test' } before do periphery.binary_path = binary('periphery') @@ -32,11 +33,11 @@ deleted_files: [], added_files: added_files ) - periphery.scan(periphery_options) end context 'when .swift files are not in diff' do it 'reports nothing' do + periphery.scan(periphery_options) expect(warnings).to be_empty end end @@ -45,6 +46,7 @@ let(:added_files) { ['test/main.swift'] } it 'reports unused code' do + periphery.scan(periphery_options) expect(warnings).to include "Function 'unusedMethod()' is unused" end end @@ -53,7 +55,16 @@ let(:modified_files) { ['test/main.swift'] } it 'reports unused code' do + periphery.scan(periphery_options) expect(warnings).to include "Function 'unusedMethod()' is unused" end end + + context 'when multiple targets are analyzed' do + let(:targets) { %w[test unit-test] } + + it 'does not raise any error' do + expect { periphery.scan(periphery_options) }.not_to raise_error + end + end end diff --git a/spec/periphery/runner_spec.rb b/spec/periphery/runner_spec.rb index fd1f7e8..81b0fd2 100644 --- a/spec/periphery/runner_spec.rb +++ b/spec/periphery/runner_spec.rb @@ -56,6 +56,14 @@ describe '#scan_arguments' do subject(:scan_arguments) { runner.scan_arguments(options) } + let(:periphery_version) { '2.18.0' } + + before do + status = instance_double(Process::Status, success?: true) + allow(Open3).to receive(:capture3).once.with(binary_path, 'version') + .and_return ["#{periphery_version}\n", '', status] + end + context 'with empty options' do let(:options) { {} } @@ -96,8 +104,18 @@ } end - it 'returns correct arguments' do - expect(scan_arguments).to eq %w[--project test.xcodeproj --targets test1,test2] + context 'with Periphery >= 2.18.0' do + it 'returns space-separated arguments' do + expect(scan_arguments).to eq %w[--project test.xcodeproj --targets test1 test2] + end + end + + context 'with Periphery < 2.18.0' do + let(:periphery_version) { '2.17.0' } + + it 'returns comma-separated arguments' do + expect(scan_arguments).to eq %w[--project test.xcodeproj --targets test1,test2] + end end end @@ -128,4 +146,22 @@ end end end + + describe '#version' do + context 'when periphery succeeds' do + it 'returns the correct version' do + status = instance_double(Process::Status, success?: true) + allow(Open3).to receive(:capture3).once.with(binary_path, 'version').and_return ["2.18.0\n", '', status] + expect(runner.version).to eq '2.18.0' + end + end + + context 'when periphery fails' do + it 'raises an error' do + status = instance_double(Process::Status, success?: false, exitstatus: 42) + allow(Open3).to receive(:capture3).once.with(binary_path, 'version').and_return ['', 'error', status] + expect { runner.version }.to raise_error(/error/) + end + end + end end diff --git a/spec/support/fixtures/test.xcodeproj/project.pbxproj b/spec/support/fixtures/test.xcodeproj/project.pbxproj index c745832..e698d9e 100644 --- a/spec/support/fixtures/test.xcodeproj/project.pbxproj +++ b/spec/support/fixtures/test.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 5B3EA09B271EE2DD00388A3A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3EA09A271EE2DD00388A3A /* main.swift */; }; + 6C2E9BD12B5E774E00133EA5 /* SomeClassTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2E9BD02B5E774E00133EA5 /* SomeClassTests.swift */; }; + 6C2E9BD62B5E7A9800133EA5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3EA09A271EE2DD00388A3A /* main.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -25,6 +27,9 @@ /* Begin PBXFileReference section */ 5B3EA097271EE2DD00388A3A /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; }; 5B3EA09A271EE2DD00388A3A /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 6C2E9BCE2B5E774E00133EA5 /* unit-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "unit-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C2E9BD02B5E774E00133EA5 /* SomeClassTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SomeClassTests.swift; sourceTree = ""; }; + 6C2E9BD72B5E7B1600133EA5 /* fixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = fixtures; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -35,13 +40,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6C2E9BCB2B5E774E00133EA5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 5B3EA08E271EE2DD00388A3A = { isa = PBXGroup; children = ( + 6C2E9BD72B5E7B1600133EA5 /* fixtures */, 5B3EA099271EE2DD00388A3A /* test */, + 6C2E9BCF2B5E774E00133EA5 /* unit-test */, 5B3EA098271EE2DD00388A3A /* Products */, ); sourceTree = ""; @@ -50,6 +64,7 @@ isa = PBXGroup; children = ( 5B3EA097271EE2DD00388A3A /* test */, + 6C2E9BCE2B5E774E00133EA5 /* unit-test.xctest */, ); name = Products; sourceTree = ""; @@ -62,6 +77,14 @@ path = test; sourceTree = ""; }; + 6C2E9BCF2B5E774E00133EA5 /* unit-test */ = { + isa = PBXGroup; + children = ( + 6C2E9BD02B5E774E00133EA5 /* SomeClassTests.swift */, + ); + path = "unit-test"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -82,18 +105,38 @@ productReference = 5B3EA097271EE2DD00388A3A /* test */; productType = "com.apple.product-type.tool"; }; + 6C2E9BCD2B5E774E00133EA5 /* unit-test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6C2E9BD42B5E774E00133EA5 /* Build configuration list for PBXNativeTarget "unit-test" */; + buildPhases = ( + 6C2E9BCA2B5E774E00133EA5 /* Sources */, + 6C2E9BCB2B5E774E00133EA5 /* Frameworks */, + 6C2E9BCC2B5E774E00133EA5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "unit-test"; + productName = "unit-test"; + productReference = 6C2E9BCE2B5E774E00133EA5 /* unit-test.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 5B3EA08F271EE2DD00388A3A /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1250; + LastSwiftUpdateCheck = 1520; LastUpgradeCheck = 1250; TargetAttributes = { 5B3EA096271EE2DD00388A3A = { CreatedOnToolsVersion = 12.5.1; }; + 6C2E9BCD2B5E774E00133EA5 = { + CreatedOnToolsVersion = 15.2; + }; }; }; buildConfigurationList = 5B3EA092271EE2DD00388A3A /* Build configuration list for PBXProject "test" */; @@ -110,10 +153,21 @@ projectRoot = ""; targets = ( 5B3EA096271EE2DD00388A3A /* test */, + 6C2E9BCD2B5E774E00133EA5 /* unit-test */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 6C2E9BCC2B5E774E00133EA5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 5B3EA093271EE2DD00388A3A /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -123,6 +177,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 6C2E9BCA2B5E774E00133EA5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C2E9BD62B5E7A9800133EA5 /* main.swift in Sources */, + 6C2E9BD12B5E774E00133EA5 /* SomeClassTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -259,6 +322,47 @@ }; name = Release; }; + 6C2E9BD22B5E774E00133EA5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.manicmaniac.unit-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6C2E9BD32B5E774E00133EA5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 13.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.manicmaniac.unit-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -280,6 +384,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 6C2E9BD42B5E774E00133EA5 /* Build configuration list for PBXNativeTarget "unit-test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6C2E9BD22B5E774E00133EA5 /* Debug */, + 6C2E9BD32B5E774E00133EA5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 5B3EA08F271EE2DD00388A3A /* Project object */; diff --git a/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/test.xcscheme b/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/test.xcscheme new file mode 100644 index 0000000..a65117b --- /dev/null +++ b/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/test.xcscheme @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/unit-test.xcscheme b/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/unit-test.xcscheme new file mode 100644 index 0000000..06a16f8 --- /dev/null +++ b/spec/support/fixtures/test.xcodeproj/xcshareddata/xcschemes/unit-test.xcscheme @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/support/fixtures/test.xctestplan b/spec/support/fixtures/test.xctestplan new file mode 100644 index 0000000..abc1e0d --- /dev/null +++ b/spec/support/fixtures/test.xctestplan @@ -0,0 +1,24 @@ +{ + "configurations" : [ + { + "id" : "766F2E70-2301-4182-9DD2-3D7FA5E41570", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:test.xcodeproj", + "identifier" : "6C2E9BCD2B5E774E00133EA5", + "name" : "unit-test" + } + } + ], + "version" : 1 +} diff --git a/spec/support/fixtures/unit-test/SomeClassTests.swift b/spec/support/fixtures/unit-test/SomeClassTests.swift new file mode 100644 index 0000000..44e9f38 --- /dev/null +++ b/spec/support/fixtures/unit-test/SomeClassTests.swift @@ -0,0 +1,4 @@ +import XCTest + +final class SomeClassTests: XCTestCase { +} diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb index 490a015..81bdc08 100644 --- a/spec/support/shared_examples.rb +++ b/spec/support/shared_examples.rb @@ -8,7 +8,7 @@ before :all do @derived_data_path = Dir.mktmpdir - system('xcodebuild', 'build', '-quiet', + system('xcodebuild', 'build-for-testing', '-quiet', '-project', fixture('test.xcodeproj'), '-scheme', 'test', '-configuration', 'Debug',