alexdunae-w3c_validators 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ module W3CValidators
2
+ class Results
3
+ attr_reader :uri, :checked_by, :doctype, :css_level, :charset, :validity, :debug_messages
4
+
5
+ def initialize(options = {})
6
+ @messages = []
7
+ @uri = options[:uri]
8
+ @checked_by = options[:checked_by]
9
+ @doctype = options[:doctype]
10
+ @css_level = options[:css_level]
11
+ @charset = options[:charset]
12
+ if options[:validity].kind_of?(String)
13
+ @validity = options[:validity].downcase.strip == 'true'
14
+ else
15
+ @validity = options[:validity]
16
+ end
17
+ @debug_messages = {}
18
+ end
19
+
20
+ def add_message(type, params = {})
21
+ uri = params[:uri] ||= @uri
22
+ @messages << Message.new(uri, type, params)
23
+ end
24
+
25
+ def add_error(params = {})
26
+ add_message(:error, params)
27
+ end
28
+
29
+ def add_warning(params = {})
30
+ add_message(:warning, params)
31
+ end
32
+
33
+ def add_debug_message(key, val)
34
+ @debug_messages[key] = val
35
+ end
36
+
37
+ # Returns either the +DOCTYPE+ or CSS level, whichever is present.
38
+ def checked_against
39
+ return @doctype if @doctype
40
+ return @css_level if @css_level
41
+ nil
42
+ end
43
+
44
+ def is_valid?
45
+ @validity
46
+ end
47
+
48
+ # Returns an array of Message objects.
49
+ def errors
50
+ errors = []
51
+ @messages.each { |msg| errors << msg if msg.is_error? }
52
+ errors
53
+ end
54
+
55
+ # Returns an array of Message objects.
56
+ def warnings
57
+ errors = []
58
+ @messages.each { |msg| errors << msg if msg.is_warning? }
59
+ errors
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,160 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'rexml/document'
5
+
6
+ require 'w3c_validators/exceptions'
7
+ require 'w3c_validators/constants'
8
+ require 'w3c_validators/results'
9
+ require 'w3c_validators/message'
10
+
11
+ module W3CValidators
12
+ # Base class for MarkupValidator and FeedValidator.
13
+ class Validator
14
+ VERSION = '0.9.3'
15
+ USER_AGENT = "Ruby W3C Validators/#{Validator::VERSION} (http://code.dunae.ca/w3c_validators/)"
16
+ HEAD_STATUS_HEADER = 'X-W3C-Validator-Status'
17
+ HEAD_ERROR_COUNT_HEADER = 'X-W3C-Validator-Errors'
18
+ SOAP_OUTPUT_PARAM = 'soap12'
19
+
20
+ attr_reader :results, :validator_uri
21
+
22
+ # Create a new instance of the Validator.
23
+ def initialize(options = {})
24
+ @options = options
25
+ end
26
+
27
+ protected
28
+ # Perform a validation request.
29
+ #
30
+ # +request_mode+ must be either <tt>:get</tt>, <tt>:head</tt> or <tt>:post</tt>.
31
+ #
32
+ # Returns Net::HTTPResponse.
33
+ def send_request(options, request_mode = :get)
34
+ response = nil
35
+ results = nil
36
+
37
+ Net::HTTP.start(@validator_uri.host, @validator_uri.port) do |http|
38
+ case request_mode
39
+ when :head
40
+ # perform a HEAD request
41
+ raise ArgumentError, "a URI must be provided for HEAD requests." unless options[:uri]
42
+ query = create_query_string_data(options)
43
+ response = http.request_head(@validator_uri.path + '?' + query)
44
+ when :get
45
+ # send a GET request
46
+ query = create_query_string_data(options)
47
+ response = http.get(@validator_uri.path + '?' + query)
48
+ when :post
49
+ # send a multipart form request
50
+ query, boundary = create_multipart_data(options)
51
+ response = http.post2(@validator_uri.path, query, "Content-type" => "multipart/form-data; boundary=" + boundary)
52
+ else
53
+ raise ArgumentError, "request_mode must be either :get, :head or :post"
54
+ end
55
+ end
56
+
57
+ response.value
58
+ return response
59
+
60
+ rescue Exception => e
61
+ handle_exception e
62
+ end
63
+
64
+ def create_multipart_data(options) # :nodoc:
65
+ boundary = '349832898984244898448024464570528145'
66
+
67
+ # added 2008-03-12: HTML5 validator expects 'file' and 'content' to be the last fields so
68
+ # we process those params separately
69
+ last_params = []
70
+
71
+ # added 2008-03-12: HTML5 validator expects 'file' instead of 'uploaded_file'
72
+ if options[:file] and !options[:uploaded_file]
73
+ options[:uploaded_file] = options[:file]
74
+ end
75
+
76
+ if options[:uploaded_file]
77
+ filename = options[:file_path] ||= 'temp.html'
78
+ content = options[:uploaded_file]
79
+ last_params << "Content-Disposition: form-data; name=\"uploaded_file\"; filename=\"#{filename}\"\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "#{content}\r\n"
80
+ options.delete(:uploaded_file)
81
+ options.delete(:file_path)
82
+ end
83
+
84
+ if options[:content]
85
+ last_params << "Content-Disposition: form-data; name=\"#{CGI::escape('content')}\"\r\n" + "\r\n" + "#{options[:content]}\r\n"
86
+ end
87
+
88
+ misc_params = []
89
+ options.each do |key, value|
90
+ if value
91
+ misc_params << "Content-Disposition: form-data; name=\"#{CGI::escape(key.to_s)}\"\r\n" + "\r\n" + "#{value}\r\n"
92
+ end
93
+ end
94
+
95
+ params = misc_params + last_params
96
+
97
+ multipart_query = params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
98
+
99
+ [multipart_query, boundary]
100
+ end
101
+
102
+ def create_query_string_data(options) # :nodoc:
103
+ qs = ''
104
+ options.each do |key, value|
105
+ if value
106
+ qs += "#{key}=" + CGI::escape(value.to_s) + "&"
107
+ end
108
+ end
109
+ qs
110
+ end
111
+
112
+ def read_local_file(file_path) # :nodoc:
113
+ fh = File.new(file_path, 'r+')
114
+ src = fh.read
115
+ fh.close
116
+ src
117
+ end
118
+
119
+ private
120
+ #--
121
+ # Big thanks to ara.t.howard and Joel VanderWerf on Ruby-Talk for the exception handling help.
122
+ #++
123
+ def handle_exception(e, msg = '') # :nodoc:
124
+ case e
125
+ when Net::HTTPServerException, SocketError
126
+ msg = "unable to connect to the validator at #{@validator_uri} (response was #{e.message})."
127
+ raise ValidatorUnavailable, msg, caller
128
+ when REXML::ParseException
129
+ msg = "unable to parse the response from the validator."
130
+ raise ParsingError, msg, caller
131
+ else
132
+ raise e
133
+ end
134
+
135
+ if e.respond_to?(:error_handler_before)
136
+ fcall(e, :error_handler_before, self)
137
+ end
138
+
139
+ if e.respond_to?(:error_handler_instead)
140
+ fcall(e, :error_handler_instead, self)
141
+ else
142
+ if e.respond_to? :status
143
+ exit_status(( e.status ))
144
+ end
145
+
146
+ if SystemExit === e
147
+ stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit') ### avoids double message for abort('message')
148
+ end
149
+ end
150
+
151
+ if e.respond_to?(:error_handler_after)
152
+ fcall(e, :error_handler_after, self)
153
+ end
154
+
155
+ exit_status(( exit_failure )) if exit_status == exit_success
156
+ exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
157
+ exit exit_status
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the CSSValidator.
4
+ class CSSValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = CSSValidator.new
8
+
9
+ @invalid_fragment = <<-EOT
10
+ a { color: white; }
11
+ body { margin: blue; }
12
+
13
+ EOT
14
+
15
+ sleep 1
16
+ end
17
+
18
+ def test_overriding_css_profile
19
+ @v.set_profile!(:svgbasic)
20
+ r = @v.validate_text(@invalid_fragment)
21
+ assert_equal 'svgbasic', r.css_level
22
+ end
23
+
24
+ def test_validating_file
25
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_css.css')
26
+ r = @v.validate_file(file_path)
27
+ assert_equal 1, r.errors.length
28
+ end
29
+
30
+ def test_validating_uri
31
+ @v.set_profile!(:svgbasic)
32
+ r = @v.validate_text(@invalid_fragment)
33
+ assert_equal 1, r.errors.length
34
+ end
35
+
36
+ def test_validating_text
37
+ r = @v.validate_text(@invalid_fragment)
38
+ assert_equal 1, r.errors.length
39
+ end
40
+
41
+ def test_validating_text_via_file
42
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_css.css')
43
+ fh = File.new(file_path, 'r+')
44
+ r = @v.validate_file(fh)
45
+ fh.close
46
+ assert_equal 1, r.errors.length
47
+ end
48
+
49
+
50
+
51
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ExceptionTests < Test::Unit::TestCase
4
+ include W3CValidators
5
+
6
+ def setup
7
+ @valid_fragment = <<-EOV
8
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
9
+ <title>Test</title>
10
+ <body>
11
+ <div class="example">This is a test</div>
12
+ </body>
13
+ EOV
14
+ end
15
+
16
+ def test_bad_validator_uri
17
+ ['http://noexist/', 'http://noexist.badtld/',
18
+ 'http://example.com/noexist'].each do |uri|
19
+ v = MarkupValidator.new(:validator_uri => uri)
20
+ assert_raise ValidatorUnavailable do
21
+ r = v.validate_text(@valid_fragment)
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_bad_soap_response
27
+ return # need to set up a test host
28
+ [].each do |uri|
29
+ v = MarkupValidator.new(:validator_uri => uri)
30
+ assert_raise ParsingError do
31
+ r = v.validate_text(@valid_fragment)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the FeedValidator.
4
+ class FeedValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = FeedValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_validating_uri_with_soap
12
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_feed.xml')
13
+ assert_equal 1, r.errors.length
14
+ assert_equal 1, r.warnings.length
15
+ end
16
+
17
+ def test_validating_file
18
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_feed.xml')
19
+ r = @v.validate_file(file_path)
20
+ assert_equal 1, r.errors.length
21
+ end
22
+
23
+ def test_validating_text
24
+ fragment = <<-EOT
25
+ <?xml version="1.0" encoding="utf-8"?>
26
+ <feed xmlns="http://www.w3.org/2005/Atom">
27
+ <title>Example Feed</title>
28
+ <subtitle>A subtitle.</subtitle>
29
+ <link href="http://example.org/feed/" rel="self"/>
30
+ <link href="http://example.org/"/>
31
+ <updated>2003-12-13T18:30:02Z</updated>
32
+ <author>
33
+ <email>johndoe@example.com</email>
34
+ </author>
35
+ <id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
36
+ <entry>
37
+ <title>Atom-Powered Robots Run Amok</title>
38
+ <link href="http://example.org/2003/12/13/atom03"/>
39
+ <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
40
+ <updated>2003-12-13T18:30:02Z</updated>
41
+ <summary>Some text.</summary>
42
+ </entry>
43
+ </feed>
44
+ EOT
45
+
46
+ r = @v.validate_text(fragment)
47
+ assert_equal 1, r.errors.length
48
+ end
49
+
50
+
51
+ def test_validating_text_via_file
52
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_feed.xml')
53
+ fh = File.new(file_path, 'r+')
54
+ r = @v.validate_file(fh)
55
+ fh.close
56
+ assert_equal 1, r.errors.length
57
+ end
58
+
59
+
60
+
61
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
2
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'w3c_validators'
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the HTML5Validator.
4
+ class HTML5ValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = NuValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_getting_request_data
12
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/valid_html5.html')
13
+ assert_equal :html5, r.doctype
14
+ assert_equal 'http://code.dunae.ca/w3c_validators/test/valid_html5.html', r.uri
15
+ assert_equal 0, r.errors.length
16
+ assert_equal 0, r.warnings.length
17
+ assert r.is_valid?
18
+ end
19
+
20
+ def test_validating_uri
21
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_html5.html')
22
+ assert_equal 1, r.errors.length
23
+ assert_equal 1, r.warnings.length
24
+ assert !r.is_valid?
25
+ end
26
+
27
+ def test_validating_file
28
+ file = File.dirname(__FILE__) + '/fixtures/invalid_html5.html'
29
+ r = @v.validate_file(file)
30
+ assert_equal 1, r.errors.length
31
+ end
32
+
33
+ def test_validating_text
34
+ valid_fragment = <<-EOV
35
+ <!DOCTYPE html>
36
+ <html lang="en-ca">
37
+ <head>
38
+ <title>HTML 5 Example</title>
39
+ </head>
40
+ <body>
41
+ <!-- should have one error (missing </section>) -->
42
+ <p>This is a sample HTML 5 document.</p>
43
+ <section>
44
+ <h1>Example of paragraphs</h1>
45
+ This is the <em>first</em> paragraph in this example.
46
+ <p>This is the second.</p>
47
+ <p>Test<br>test</p>
48
+ </body>
49
+ </html>
50
+ EOV
51
+
52
+ r = @v.validate_text(valid_fragment)
53
+ assert_equal 1, r.errors.length
54
+ end
55
+
56
+ #def test_validating_text_via_file
57
+ # fh = File.new(File.dirname(__FILE__) + '/fixtures/invalid_html5.html', 'r+')
58
+ # r = @v.validate_file(fh)
59
+ # fh.close
60
+ # assert_equal 1, r.errors.length
61
+ #end
62
+
63
+
64
+ end
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the MarkupValidator.
4
+ class MarkupValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = MarkupValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_overriding_doctype
12
+ @v.set_doctype!(:html32, false)
13
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
14
+ assert_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
15
+ end
16
+
17
+ def test_overriding_doctype_for_fallback_only
18
+ @v.set_doctype!(:html32, true)
19
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
20
+ assert_not_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
21
+ end
22
+
23
+ def test_overriding_charset
24
+ @v.set_charset!(:utf_16, false)
25
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
26
+ assert_equal 'utf-16', r.charset
27
+ end
28
+
29
+ def test_overriding_charset_for_fallback_only
30
+ @v.set_doctype!(:utf_16, true)
31
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
32
+ assert_not_equal 'utf-16', r.charset
33
+ end
34
+
35
+ def test_validating_uri_with_head_request
36
+ r = @v.validate_uri_quickly('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
37
+ assert_equal 1, r.errors.length
38
+ assert_equal 0, r.warnings.length
39
+ end
40
+
41
+ def test_validating_uri_with_soap
42
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
43
+ assert_equal 1, r.errors.length
44
+ assert_equal 0, r.warnings.length
45
+ end
46
+
47
+ def test_debugging_uri
48
+ @v.set_debug!
49
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
50
+ assert r.debug_messages.length > 0
51
+ end
52
+
53
+ def test_validating_file
54
+ file = File.dirname(__FILE__) + '/fixtures/invalid_markup.html'
55
+ r = @v.validate_file(file)
56
+ assert_equal 1, r.errors.length
57
+
58
+ assert r.uri =~ /fixtures\/invalid_markup\.html$/
59
+ end
60
+
61
+ def test_validating_text
62
+ valid_fragment = <<-EOV
63
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
64
+ <title>Test</title>
65
+ <body>
66
+ <div class="example">This is a test</div>
67
+ </body>
68
+ EOV
69
+
70
+ r = @v.validate_text(valid_fragment)
71
+ assert_equal 0, r.errors.length
72
+ assert_equal 0, r.warnings.length
73
+ end
74
+
75
+ def test_validating_text_via_file
76
+ fh = File.new(File.dirname(__FILE__) + '/fixtures/invalid_markup.html', 'r+')
77
+ r = @v.validate_file(fh)
78
+ fh.close
79
+ assert_equal 1, r.errors.length
80
+ end
81
+
82
+
83
+ def test_validator_abort
84
+ @v.set_debug!
85
+ assert_nothing_raised do
86
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_encoding.html')
87
+ assert !r.is_valid?
88
+ assert_equal 1, r.errors.length
89
+ assert_equal [], r.warnings
90
+ end
91
+ end
92
+
93
+
94
+ end