Skip to content
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

Introduce test_specification DSL #369

Merged
merged 1 commit into from
Apr 24, 2017
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

##### Enhancements

* None.
* Introduce `test_specification` DSL
[Dimitris Koutsogiorgas](https://github.com/dnkoutso)
[Kyle Fuller](https://github.com/kylef)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the work was done so I included the credit.

[#369](https://github.com/CocoaPods/Core/pull/369)

##### Bug Fixes

Expand Down
36 changes: 29 additions & 7 deletions lib/cocoapods-core/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ class Specification
# @param [String] name
# the name of the specification.
#
def initialize(parent = nil, name = nil)
# @param [Bool] test_specification
# Whether the specification is a test specification
#
def initialize(parent = nil, name = nil, test_specification = false)
@attributes_hash = {}
@subspecs = []
@consumers = {}
@parent = parent
@hash_value = nil
@test_specification = test_specification
attributes_hash['name'] = name

yield self if block_given?
Expand Down Expand Up @@ -203,7 +207,26 @@ def subspec?

# @!group Dependencies & Subspecs

# @return [Array<Specifications>] the recursive list of all the subspecs of
# @return [Bool] if the specification is a test specification
#
def test_specification?
@test_specification
end

# @return [Symbol] the test type supported if this is a test specification
#
def test_type
attributes_hash['test_type']
end

# @return [Array<Specification>] the list of all the test subspecs of
# a specification.
#
def test_specs
subspecs.select(&:test_specification?)
end

# @return [Array<Specification>] the recursive list of all the subspecs of
# a specification.
#
def recursive_subspecs
Expand Down Expand Up @@ -241,7 +264,7 @@ def subspec_by_name(relative_name, raise_if_missing = true)
else
remainder = relative_name[base_name.size + 1..-1]
subspec_name = remainder.split('/').shift
subspec = subspecs.find { |s| s.base_name == subspec_name }
subspec = subspecs.find { |s| s.base_name == subspec_name && !s.test_specification? }
unless subspec
if raise_if_missing
raise Informative, 'Unable to find a specification named ' \
Expand Down Expand Up @@ -274,7 +297,7 @@ def default_subspecs
#
def subspec_dependencies(platform = nil)
specs = if default_subspecs.empty?
subspecs.compact
subspecs.compact.reject(&:test_specification?)
else
default_subspecs.map do |subspec_name|
root.subspec_by_name("#{name}/#{subspec_name}")
Expand All @@ -288,9 +311,8 @@ def subspec_dependencies(platform = nil)

# Returns the dependencies on other Pods or subspecs of other Pods.
#
# @param [Bool] all_platforms
# whether the dependencies should be returned for all platforms
# instead of the active one.
# @param [Platform] platform
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed documentation for this method.

# return only dependencies supported on the given platform.
#
# @note External dependencies are inherited by subspecs
#
Expand Down
8 changes: 8 additions & 0 deletions lib/cocoapods-core/specification/consumer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ def user_target_xcconfig

#-----------------------------------------------------------------------#

# @!group Test Support

# @return [Symbol] the test type supported by this Pod.
#
spec_attr_accessor :test_type

#-----------------------------------------------------------------------#

# @!group File patterns

# @return [Array<String>] the source files of the Pod.
Expand Down
41 changes: 41 additions & 0 deletions lib/cocoapods-core/specification/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,47 @@ def subspec(name, &block)
subspec
end

# The list of the test types currently supported.
#
SUPPORTED_TEST_TYPES = [:unit].freeze

# The test type this specification supports. This only applies to test specifications.
#
# ---
#
# @example
#
# test_spec.test_type = :unit
#
# @param [Symbol] type
# The test type to use.
attribute :test_type,
Copy link
Contributor Author

@dnkoutso dnkoutso Apr 22, 2017

Choose a reason for hiding this comment

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

@orta @dantoml I am reading the code more I dont think :keys is what we want here so I removed it. Keys is meant to be used to validate the keys within a hash value, not for limiting the potential values you can have for this attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead the Linter takes care of validating the value is correct, which I added a test for.

:default_value => :unit,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Turns out this exists!

:types => [Symbol],
:multi_platform => false

# Represents a test specification for the library. Here you can place all
# your tests for your podspec along with the test dependencies.
#
# ---
#
# @example
#
# Pod::Spec.new do |spec|
# spec.name = 'NSAttributedString+CCLFormat'
#
# spec.test_spec do |test_spec|
# test_spec.source_files = 'NSAttributedString+CCLFormatTests.m'
# test_spec.dependency 'Expecta'
# end
# end
#
def test_spec(name = 'Tests', &block)
subspec = Specification.new(self, name, true, &block)
@subspecs << subspec
subspec
end

#------------------#

# @!method default_subspecs=(subspec_array)
Expand Down
8 changes: 7 additions & 1 deletion lib/cocoapods-core/specification/linter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def run_root_validation_hooks
run_validation_hooks(attributes, spec)
end

# Run validations for multi-platform attributes activating .
# Run validations for multi-platform attributes activating.
#
# @return [void]
#
Expand Down Expand Up @@ -364,6 +364,12 @@ def _validate_deprecated_in_favor_of(d)
end
end

def _validate_test_type(t)
supported_test_types = Specification::DSL::SUPPORTED_TEST_TYPES
results.add_error('test_type', "The test type `#{t}` is not supported. " \
"Supported test type values are #{supported_test_types}.") unless supported_test_types.include?(t)
end

# Performs validations related to github sources.
#
def perform_github_source_checks(s)
Expand Down
7 changes: 7 additions & 0 deletions spec/specification/consumer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ module Pod
@spec.header_mappings_dir = 'src/include'
@subspec_consumer.header_mappings_dir.should == 'src/include'
end

#------------------#

it 'allows to specify the test type' do
@subspec.test_type = :unit
@subspec_consumer.test_type.should == :unit
end
end

#-------------------------------------------------------------------------#
Expand Down
21 changes: 21 additions & 0 deletions spec/specification/dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,27 @@ module Pod

#-----------------------------------------------------------------------------#

describe 'Test specs' do
before do
@spec = Spec.new do |spec|
spec.name = 'Spec'
spec.test_spec do |test_spec|
test_spec.test_type = :unit
Copy link
Contributor Author

@dnkoutso dnkoutso Apr 21, 2017

Choose a reason for hiding this comment

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

@orta @dantoml if I a podspec author does not call this, how can I enforce a default value? Is it on the Specification constructor that I add test_spec = :unit if test_specification or something akin to that?

Copy link
Contributor Author

@dnkoutso dnkoutso Apr 21, 2017

Choose a reason for hiding this comment

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

added it in the initializer of Specification.rb

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed it now as the attribute now has a default_value set when used with a consumer.rb.

end
end
end

it 'allows you to specify a test spec' do
test_spec = @spec.subspecs.first
test_spec.class.should == Specification
test_spec.name.should == 'Spec/Tests'
test_spec.test_specification?.should == true
test_spec.test_type.should == :unit
end
end

#-----------------------------------------------------------------------------#

describe 'Multi-Platform' do
before do
@spec = Spec.new do |s|
Expand Down
13 changes: 13 additions & 0 deletions spec/specification/linter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,19 @@ def result_should_include(*values)

#------------------#

it 'checks the test type' do
podspec = 'Pod::Spec.new do |s|; s.test_spec do |ts|; ts.test_type = :unknown; end end'
path = SpecHelper.temporary_directory + 'BananaLib.podspec'
File.open(path, 'w') { |f| f.write(podspec) }
linter = Specification::Linter.new(path)
linter.lint
results = linter.results
test_type_error = results.find { |result| result.to_s.downcase.include?('test_type') }
test_type_error.message.should.include?('The test type `unknown` is not supported.')
end

#------------------#

it 'checks if the description is not an empty string' do
@spec.stubs(:description).returns('')
result_should_include('description', 'empty')
Expand Down
35 changes: 35 additions & 0 deletions spec/specification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,23 +217,30 @@ module Pod
s.name = 'Pod'
s.subspec 'Subspec' do |_sp|
end
s.test_spec do |_tsp|
end
end
@subspec = @spec.subspecs.first
@test_subspec = @spec.test_specs.first
end

it 'returns the root spec' do
@spec.root.should == @spec
@subspec.root.should == @spec
@test_subspec.root.should == @spec
end

it 'returns whether it is a root spec' do
@spec.root?.should.be.true
@subspec.root?.should.be.false
@test_subspec.root?.should.be.false
end

it 'returns whether it is a subspec' do
@spec.subspec?.should.be.false
@subspec.subspec?.should.be.true
@test_subspec.subspec?.should.be.true
@test_subspec.test_specification?.should.be.true
end
end

Expand Down Expand Up @@ -273,6 +280,17 @@ module Pod
@spec.subspec_by_name('Pod/Subspec/Subsubspec').should == @subsubspec
end

it "doesn't return the test subspec given the Tests name" do
@spec = Spec.new do |s|
s.name = 'Pod'
s.version = '1.0'
s.dependency 'AFNetworking'
s.osx.dependency 'MagicalRecord'
s.test_spec {}
end
@spec.subspec_by_name('Pod/Tests', false).should. nil?
end

it 'returns a subspec given the relative name' do
@subspec.subspec_by_name('Subspec/Subsubspec').should == @subsubspec
end
Expand Down Expand Up @@ -327,12 +345,29 @@ module Pod
]
end

it 'excludes the test subspec from the subspec dependencies' do
@spec.test_spec {}
@spec.subspec_dependencies.sort.should == [
Dependency.new('Pod/Subspec', '1.0'),
Dependency.new('Pod/SubspecOSX', '1.0'),
Dependency.new('Pod/SubspeciOS', '1.0')]
end

it 'returns all the dependencies' do
@spec.dependencies.sort.should == [
Dependency.new('AFNetworking'),
Dependency.new('MagicalRecord')]
end

it 'returns the test spec dependencies' do
test_spec = @spec.test_spec { |s| s.dependency 'OCMock' }
test_spec.dependencies.sort.should == [
Dependency.new('AFNetworking'),
Dependency.new('MagicalRecord'),
Dependency.new('OCMock'),
]
end

it 'returns the dependencies given the platform' do
@spec.dependencies(:ios).sort.should == [Dependency.new('AFNetworking')]
end
Expand Down