CodeMonkeySteve-assert_valid_content 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2006-2009 Steve Sloan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.rdoc ADDED
@@ -0,0 +1,74 @@
1
+ = assert_valid_content
2
+
3
+ assert_valid_content is a RubyGem which validates a variety content types (e.g. (X)HTML, CSS, XML) as part of your unit or functional tests.
4
+ It is inspired by assert_valid_markup[http://github.com/wr0ngway/assert_valid_markup] and
5
+ assert_valid_assert[http://www.realityforge.org/articles/2006/03/15/rails-plugin-to-validate-x-html-and-css] with the following goals:
6
+
7
+ * Allow for offline use (i.e. not reliant on web services)
8
+ * Validate a wide variety of content types: HTML, JS, JPEG, PDF, MP3, ad infinitum
9
+ * Packaged as a RubyGem, instead of a Rails plugin
10
+ * Provide for specification of more-stringent validation checks (e.g. image properties)
11
+
12
+ == Validation Modules
13
+
14
+ === Currently supported:
15
+ [(X)HTML, XML] LibXML via libxml-ruby[http://libxml.rubyforge.org/]
16
+ [CSS] W3C CSS Validator Service[http://jigsaw.w3.org/css-validator]
17
+
18
+ === Planned support:
19
+ [CSS] W3C CSS Validator (local execution)
20
+ [JPEG, PNG, et al.] ImageMagick via rmagick[http://rmagick.rubyforge.org]
21
+ [MP3, OGG, et al.] ruby-mp3info[http://ruby-mp3info.rubyforge.org/]
22
+ [PDF, MPEG, etc.] ?
23
+
24
+ == Usage
25
+
26
+ Validation is performed primary through dynamic assertion functions, defined in AssertValidContent::Assertions
27
+ (and automatically included into Test::Unit::TestCase, if it exists). The assertions are of the form:
28
+
29
+ assert_valid_TYPE( content )
30
+
31
+ where +TYPE+ is a MIME type name (e.g. "html", "css", etc.), and +content+ is a +String+, +Pathname+, or +IO+ object,
32
+ or any combination in an +Array+. If no +content+ is specified, and the caller is a Rails functional test, the
33
+ response body is used.
34
+
35
+ There are also a set of basic assertions:
36
+
37
+ assert_valid_content( type, content )
38
+ valid_content?( type, content ) # yields validator on invalid content
39
+
40
+ where +type+ is a MIME type constant (e.g. "Mime::HTML").
41
+
42
+ == Examples
43
+ require 'rubygems'
44
+ require 'test/unit'
45
+ require 'assert_valid_content'
46
+
47
+ class ContentTest < Test::Unit::TestCase
48
+ # simple test
49
+ def test_html
50
+ html = "<html>...</html>"
51
+
52
+ # these are all equivalent
53
+ assert_valid_html html
54
+ assert_valid_html StringIO.new(html)
55
+ assert_valid_content Mime::HTML, html
56
+ assert valid_content?(Mime::HTML, html)
57
+ end
58
+
59
+ # Rails controller test
60
+ def test_index
61
+ get :index
62
+ assert_response :success
63
+ assert_valid_html
64
+ end
65
+
66
+ # static content test
67
+ assert_valid_css Dir.glob("#{RAILS_ROOT}/public/stylesheets/*.css")
68
+ end
69
+
70
+
71
+ Author:: Steve Sloan (mailto:steve@finagle.org)
72
+ Website:: http://github.com/CodeMonkeySteve/assert_valid_content
73
+ Copyright:: Copyright (c) 2009 Steve Sloan
74
+ License:: MIT
@@ -0,0 +1,28 @@
1
+ require 'tmpdir'
2
+
3
+ module AssertValidContent
4
+ TempDir = if defined? RAILS_ROOT
5
+ File.join RAILS_ROOT, 'tmp/validate'
6
+ else
7
+ File.join Dir.tmpdir, 'assert_valid_content'
8
+ end
9
+ end
10
+
11
+ require 'assert_valid_content/libxml.rb'
12
+ if AssertValidContent::LibXML::Installed
13
+ AssertValidContent::LibXML::HTML.validates Mime::HTML
14
+ AssertValidContent::LibXML::XML. validates Mime::XML
15
+ end
16
+
17
+ require 'assert_valid_content/w3c-net.rb'
18
+ if AssertValidContent::W3C::Installed
19
+ AssertValidContent::W3C::CSS.validates Mime::CSS
20
+ end
21
+
22
+ require 'assert_valid_content/assertions.rb'
23
+ if defined? Test::Unit::TestCase
24
+ module Test # :nodoc:all
25
+ class Test::Unit::TestCase ; include AssertValidContent::Assertions ; end
26
+ end
27
+ end
28
+
@@ -0,0 +1,64 @@
1
+ require 'assert_valid_content/validator'
2
+
3
+ module AssertValidContent
4
+
5
+ # Test case assertions
6
+ module Assertions
7
+
8
+ # Asserts that the content (from one ore more +srcs+) is valid for type +type+.
9
+ # Each of +srcs+ is one of +String+, +Pathname+, or +IO+ object.
10
+ def assert_valid_content( type, *srcs )
11
+ msg = nil
12
+ for src in srcs
13
+ res = AssertValidContent.valid_content?(type, src) { |val| msg = val.to_s }
14
+ assert_block( msg ) { res }
15
+ end
16
+ end
17
+
18
+ alias_method :orig_method_missing, :method_missing #:nodoc:
19
+
20
+ # Implements the +assert_valid_TYPE+(*+srcs+) dynamic assertions, where +TYPE+ is a MIME type name
21
+ # (e.g. +html+, +css+, etc.) and each of +srcs+ one of +String+, +Pathname+, or +IO+ object.
22
+ def method_missing( name, *args )
23
+ n = name.to_s
24
+ return orig_method_missing( name, *args ) unless
25
+ (n[0..12] == 'assert_valid_') and (type = Mime::Type.lookup_by_extension n[13..-1])
26
+
27
+ if args.empty? and self.respond_to?(:response) and self.response.respond_to(:body)
28
+ args = [ self.response.body ]
29
+ end
30
+
31
+ assert_valid_content type, *args
32
+ end
33
+
34
+ module ClassMethods
35
+ alias_method :orig_method_missing, :method_missing #:nodoc:
36
+
37
+ # Implements the class-level +assert_valid_TYPE+(*+srcs+) dynamic assertions. See Assertions.method_missing.
38
+ def method_missing( name, *args )
39
+ n = name.to_s
40
+ return orig_method_missing( name, *args ) unless
41
+ (n[0..12] == 'assert_valid_') and (type = Mime::Type.lookup_by_extension n[13..-1])
42
+
43
+ self.assert_valid_static_content << [ type, *args ]
44
+ end
45
+
46
+ def assert_valid_static_content #:nodoc:
47
+ @assert_valid_static_content ||= []
48
+ end
49
+ end
50
+
51
+ def self.included( base ) #:nodoc:
52
+ base.extend Assertions::ClassMethods
53
+ end
54
+
55
+ # Executes class-level assertions (i.e. static content).
56
+ def test__assert_valid_static_content #:nodoc:
57
+ for args in self.class.assert_valid_static_content
58
+ assert_valid_content *args
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,38 @@
1
+ require 'assert_valid_content/validator'
2
+
3
+ # Validates using LibXML via the libxml-ruby[http://libxml.rubyforge.org/] gem.
4
+ module AssertValidContent::LibXML
5
+
6
+ # +true+ if required dependencies are installed
7
+ Installed = begin
8
+ gem( 'libxml-ruby', '~> 1.1' )
9
+ require 'libxml'
10
+ true
11
+ rescue Gem::LoadError
12
+ false
13
+ end
14
+
15
+ # Validates HTML and XHTML
16
+ class AssertValidContent::LibXML::HTML < AssertValidContent::Validator
17
+ def validate( content ) #:nodoc:
18
+ ::LibXML::XML::Error.set_handler { |err| @errors << err }
19
+ begin
20
+ doc = self.parser( content ).parse
21
+ rescue ::LibXML::XML::Error ; end
22
+ end
23
+
24
+ protected
25
+ def parser( content ) #:nodoc:
26
+ ::LibXML::XML::HTMLParser.string content
27
+ end
28
+ end
29
+
30
+ # Validates XML
31
+ class AssertValidContent::LibXML::XML < HTML
32
+ protected
33
+ def parser( content ) #:nodoc:
34
+ ::LibXML::XML::Parser.string content
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,56 @@
1
+ require 'action_controller' # for Mime::Type
2
+ require 'pathname'
3
+
4
+ module AssertValidContent # :nodoc
5
+
6
+ # Hash of +type+ to sets of Validator objects for that type.
7
+ def self.validators ; @@validators ; end
8
+ @@validators = Hash.new { |h, k| h[k] = Set.new }
9
+
10
+ # Returns +true+ if the content from +src+ is valid for type +type+. Yields on failure.
11
+ # +src+ is one of +String+, +Pathname+, or +IO+ object
12
+ def self.valid_content?( type, src ) # :yields: validator
13
+ validators = AssertValidContent.validators[type]
14
+ raise "No validators for type: #{type}" if validators.empty?
15
+
16
+ content = case src
17
+ when String then src
18
+ when Pathname then IO.read src
19
+ when IO then src.read
20
+ else raise "Unknown content src type: #{src.class}"
21
+ end
22
+
23
+ val = nil
24
+ valid = validators.all? do |cl|
25
+ val = cl.new
26
+ val.validate content
27
+ val.errors.empty?
28
+ end
29
+
30
+ yield(val) if block_given? and not valid
31
+ valid
32
+ end
33
+
34
+ # Base class for all content validators
35
+ class Validator
36
+ # Validation errors
37
+ attr_reader :errors
38
+
39
+ def initialize #:nodoc:
40
+ @errors = []
41
+ end
42
+
43
+ # Returns an error message listing validation errors (or +nil+ if there were none).
44
+ def to_s
45
+ return nil unless @errors
46
+ "#{self.class.name} validation failed:\n" + @errors.map { |e| " #{e.to_s}" }.join("\n")
47
+ end
48
+
49
+ protected
50
+ # Register a derived class as a validator for content of type +type+
51
+ def self.validates( type )
52
+ AssertValidContent.validators[type] << self
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,73 @@
1
+ require 'assert_valid_content/validator'
2
+ require 'md5'
3
+ require 'fileutils'
4
+ require 'cgi'
5
+ require 'net/http'
6
+
7
+ # Validates using W3C Web Service(s)
8
+ module AssertValidContent::W3C
9
+
10
+ # +false+ if +NONET+ environment variable is defined (and non-zero).
11
+ Installed = ENV['NONET'].to_i.zero?
12
+
13
+ # Validates via the W3C CSS validator service[http://jigsaw.w3.org/css-validator/]
14
+ # (taken from assert-valid-asset[http://github.com/CodeMonkeySteve/assert-valid-asset]).
15
+ class CSS < AssertValidContent::Validator
16
+
17
+ SrvHost = ENV['CSS_VALIDATOR_HOST'] || 'jigsaw.w3.org'
18
+ SrvPath = ENV['CSS_VALIDATOR_PATH'] || '/css-validator/validator'
19
+ TempDir = File.join AssertValidContent::TempDir, 'w3c-net'
20
+
21
+ def validate( content ) #:nodoc:
22
+ FileUtils.mkdir_p TempDir
23
+
24
+ md5 = MD5.md5( content ).to_s
25
+ resp_path = File.join TempDir, md5
26
+
27
+ begin
28
+ resp = File.read resp_path
29
+
30
+ rescue
31
+ params = [
32
+ file_to_multipart( 'file', 'file.css', 'text/css', content ),
33
+ text_to_multipart( 'warning', '1' ),
34
+ text_to_multipart( 'profile', 'css2' ),
35
+ text_to_multipart( 'usermedium', 'all' ),
36
+ ]
37
+ boundary = '-----------------------------24464570528145'
38
+ query = params.collect { |p| "--#{boundary}\r\n#{p}" }.join + "--#{boundary}--\r\n"
39
+ type = "multipart/form-data; boundary=#{boundary}"
40
+
41
+ resp = http.start(SrvHost).post2( SrvPath, query, 'Content-type' => type ).body
42
+ File.open( resp_path, 'w+' ) { |f| f.write resp }
43
+ end
44
+
45
+ begin
46
+ root = REXML::Document.new( resp ).root
47
+ REXML::XPath.each( root, "//x:tr[@class='error']", { "x"=>"http://www.w3.org/1999/xhtml" } ) do |el|
48
+ @errors << "Invalid CSS: line" + el.to_s.gsub(/<[^>]+>/,' ').gsub(/\n/,' ').gsub(/\s+/, ' ')
49
+ end
50
+ rescue REXML::ParseException
51
+ end
52
+ end
53
+
54
+ def text_to_multipart(key,value) #:nodoc:
55
+ "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n\r\n#{value}\r\n"
56
+ end
57
+
58
+ def file_to_multipart(key,filename,mime_type,content) #:nodoc:
59
+ "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"; filename=\"#{filename}\"\r\n" +
60
+ "Content-Transfer-Encoding: binary\r\nContent-Type: #{mime_type}\r\n\r\n#{content}\r\n"
61
+ end
62
+
63
+ def http #:nodoc:
64
+ if Module.constants.include?('ApplicationConfig') and ApplicationConfig.respond_to?(:proxy_config)
65
+ Net::HTTP::Proxy ApplicationConfig.proxy_config['host'], ApplicationConfig.proxy_config['port']
66
+ else
67
+ Net::HTTP
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: CodeMonkeySteve-assert_valid_content
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Steve Sloan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: actionpack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Provides test assertions for the validity of various content types (i.e. HTML, XML CSS, etc).
26
+ email: steve@finagle.org
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - README.rdoc
35
+ - MIT-LICENSE
36
+ - lib/assert_valid_content.rb
37
+ - lib/assert_valid_content/validator.rb
38
+ - lib/assert_valid_content/assertions.rb
39
+ - lib/assert_valid_content/libxml.rb
40
+ - lib/assert_valid_content/w3c-net.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/CodeMonkeySteve/assert_valid_content
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - --main
48
+ - README.rdoc
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Content validation test suite
70
+ test_files: []
71
+