eeml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ #Provides some helper methods for modifying xml (as plain text). (removing and replacing nodes)
2
+ #so far, this module knows nothing about libxml, only about xml (and very roughly)
3
+
4
+ #This is intended for inclusion in Test::Unit::TestCase.
5
+ #If you're not mixing it in to such a context,
6
+ #note that it will need the assertions definitions to work:
7
+ #require 'test/unit/assertions'
8
+ #include Test::Unit::Assertions
9
+ module LibXMLTestHelper
10
+ #from given xml snippet str, removes first encountered node with the given name.
11
+ #e.g. ALL of FIRST title AND nested in the following
12
+ #<title>text here<n>other stuff</n></title><other_stuff><title>this one untouched</title>...
13
+ def remove_first_node_called(name, original_xml)
14
+ return remove_nodes_called(name, original_xml, true)
15
+ end
16
+
17
+ #from given xml snippet str, removes either first node or all nodes encountered with the given name.
18
+ def remove_nodes_called(name, original_xml, limit_to_first = false)
19
+ new_xml = replace_nodes_called(name, original_xml, '', limit_to_first)
20
+ assert_not_equal original_xml.gsub(/\n\w*/,''), new_xml.gsub(/\n\w*/,''), "shouldn't be equal - should have stripped '#{name}' tag"
21
+ return new_xml
22
+ end
23
+
24
+ #from given xml snippet str, replaces the first node with the given name, with the given contents.
25
+ def replace_first_node_called(name, original_xml, replacement_string)
26
+ replace_nodes_called(name, original_xml, replacement_string, true)
27
+ end
28
+
29
+ #from given xml snippet str, replaces either first node or all nodes encountered (of the given name) with the given contents.
30
+ def replace_nodes_called(name, original_xml, replacement_string, limit_to_first = false)
31
+ operation = limit_to_first ? :sub : :gsub
32
+ assert_not_nil original_xml, 'given nil for original xml to work with'
33
+ new_xml = original_xml.send(operation, /<#{name}.*?<\/#{name}>/m,replacement_string)#m is multiline. #*? is don't be greedy or we'll get more than first.
34
+ assert_not_equal original_xml.gsub(/\n\w*/,''), new_xml.gsub(/\n\w*/,''), "shouldn't be equal - should have replaced '#{name}' tag with #{replacement_string}"
35
+ assert_not_nil new_xml, 'substitution returned nil - something went wrong'
36
+ return new_xml
37
+ end
38
+ end
39
+
@@ -0,0 +1,370 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ # spec sketch is here:
4
+ # http://mulube.unfuddle.com/projects/9384/notebooks/2319/pages/102977/latest
5
+ #TODO: split this into separate files, testing the xml parsing, the xml generation, etc.
6
+ class TestEnvironment < Test::Unit::TestCase
7
+ include LibXMLTestHelper
8
+
9
+
10
+ #TODO: this is mostly testing eeml V005 parser, not environment generally
11
+
12
+ test "creating Env obj from unparseable XML" do
13
+ assert_raises BadXML do
14
+ environment = Environment.new_from_eeml("this is bad xml")
15
+ end
16
+ assert_raises BadXML do
17
+ environment = Environment.new_from_eeml("12, 14, 99, a csv!, 99")
18
+ end
19
+ end
20
+
21
+ # Create an object from an eeml document
22
+ test 'creating Env obj - attrs' do
23
+ env = create_env_from_xml_file_one
24
+
25
+ # simple text-nodes available as attributes
26
+ assert_equal('title here', env.title)
27
+ assert_equal('description here', env.description)
28
+ assert_equal('http://example.com/api/1247.xml', env.feed_url)
29
+ assert_equal('http://example.com/studio/', env.website)
30
+ assert_equal('frozen', env.status)
31
+ assert_equal('someone@example.com', env.email)
32
+ assert_equal('http://example.com/some/icon.gif', env.icon)
33
+ #env attrs
34
+ assert_equal('1247', env.identifier)
35
+ assert_equal('2009-02-11T10:56:56Z', env.updated.strftime("%Y-%m-%dT%H:%M:%SZ"))
36
+ assert_equal('http://example.com/creator/', env.creator)
37
+ end
38
+
39
+ test 'location parses ok' do
40
+ env = create_env_from_xml_file_one
41
+
42
+ #<location domain="physical" exposure="outdoor" disposition="mobile">
43
+ # <name>Up on the roof (somewhere)</name>
44
+ # <lat>50.1</lat>
45
+ # <lon>48.7</lon>
46
+ # <ele>1.34</ele>
47
+ #</location>
48
+ assert_not_nil env.location
49
+ location = env.location
50
+ assert_equal "Up on the roof (somewhere)", location.name
51
+ assert_equal "50.1", location.latitude
52
+ assert_equal "48.7", location.longitude
53
+ assert_equal "1.34", location.elevation
54
+ assert_equal "physical", location.domain
55
+ assert_equal "outdoor", location.exposure
56
+ assert_equal "mobile", location.disposition
57
+ end
58
+
59
+
60
+ test "datastreams parse ok" do
61
+ env = create_env_from_xml_file_one
62
+
63
+ assert_equal 3, env.datastreams.size
64
+
65
+ #the expected values for the datastreams in the test doc
66
+ expectations_list = [
67
+ #id, tag array, min, max, value, unit_symbol, unit_type, unit_value
68
+ ['0', ['tagD0'], '-9999.0', '1022.0', '0', 'C', 'basicSI', 'Celsius'],
69
+ ['1', [], '0.0', '1023.0', '33', nil, nil, nil],
70
+ ['2', ['tagD2a', 'tagD2b', 'tagD2c'], '23.4', '1021.0', '42.1', nil, nil, nil]
71
+ ]
72
+ env.datastreams.each_with_index do |ds, i|
73
+
74
+ e_identifier, e_tags, e_min_value, e_max_value, e_value, e_unit_symbol, e_unit_type, e_unit_value = expectations_list[i]
75
+
76
+ assert_equal e_identifier, ds.identifier
77
+
78
+ assert_equal e_tags.size, ds.tags.size, "should have right num of tags"
79
+
80
+ e_tags.each do |expected_tag|
81
+ ds.tags.member? expected_tag
82
+ end
83
+
84
+ assert_equal e_min_value, ds.min_value
85
+ assert_equal e_max_value, ds.max_value
86
+ assert_equal e_value, ds.value
87
+ assert_equal e_unit_symbol, ds.unit_symbol
88
+ assert_equal e_unit_type, ds.unit_type
89
+ assert_equal e_unit_value, ds.unit_value
90
+ end
91
+
92
+ end
93
+
94
+ implement "should not try to parse huge xml string" do
95
+ #TODO: arguably, fetcher should also have rejected huge string
96
+ end
97
+
98
+ # should this actually pass? Location is essentially a datastream in it's own right, so a feed
99
+ # that only specified location should probably be alright.
100
+ test "passes if no datastreams" do
101
+ original_xml = File.read('test/data/doc_1.xml')
102
+ test_xml = remove_nodes_called('data', original_xml)
103
+
104
+ environment = Environment.new_from_eeml(test_xml)
105
+
106
+ assert_equal('title here', environment.title)
107
+ end
108
+
109
+ test "fail if any datastream has no value" do
110
+ test_xml = remove_first_node_called('value', read_xml_file_one)
111
+ assert_raises(DataMissingValue) do
112
+ Environment.new_from_eeml(test_xml)
113
+ end
114
+ end
115
+
116
+ test "fail if any datastream has two values" do
117
+ new_value_snippet=<<-END
118
+ <value minValue="20" maxValue="909">10</value>
119
+ <value minValue="-2" maxValue="9.0">14</value>
120
+ END
121
+ original_xml = read_xml_file_one
122
+ test_xml = replace_first_node_called('value', original_xml, new_value_snippet)
123
+ assert_raises(DataHasMultipleValues) { Environment.new_from_eeml(test_xml) }
124
+ end
125
+
126
+ test "fail if any datastream has two units" do
127
+ new_unit_snippet =<<-END
128
+ <unit symbol="C" type="basicSI">Celsius</unit>
129
+ <unit symbol="C" type="basicSI">Celsius</unit>
130
+ END
131
+ original_xml = read_xml_file_one
132
+ test_xml = replace_first_node_called('unit', original_xml, new_unit_snippet)
133
+ assert_raises(DataHasMultipleUnits) { Environment.new_from_eeml(test_xml) }
134
+ end
135
+
136
+ test "ok if no location" do
137
+ original_xml = read_xml_file_one
138
+ test_xml = remove_first_node_called('location', original_xml)
139
+
140
+ env = Environment.new_from_eeml(test_xml)
141
+
142
+ # all other attributes still accessible
143
+ assert_equal('title here', env.title)
144
+ assert_equal('description here', env.description)
145
+ assert_equal('http://example.com/api/1247.xml', env.feed_url)
146
+ assert_equal('http://example.com/studio/', env.website)
147
+ assert_equal('frozen', env.status)
148
+ assert_equal('someone@example.com', env.email)
149
+ assert_equal('http://example.com/some/icon.gif', env.icon)
150
+ #env attrs
151
+ assert_equal('1247', env.identifier)
152
+ assert_equal('2009-02-11T10:56:56Z', env.updated.strftime(XML_TIME_FORMAT_STRING))
153
+ assert_equal('http://example.com/creator/', env.creator)
154
+ end
155
+
156
+ test "ok parsing most minimal eeml file" do
157
+ env = create_env_from_xml_file('minimal.xml')
158
+ assert_equal 1, env.datastreams.size
159
+
160
+ datastream = env.datastreams[0]
161
+ assert_equal "0", datastream.identifier
162
+ # this value.value is a bit ugly
163
+ assert_equal "36.2", datastream.value
164
+ end
165
+
166
+ def read_xml_file_one
167
+ return File.read('test/data/doc_1.xml')
168
+ end
169
+
170
+ test "parses_all_previously_acceptable_feeds" do
171
+ passed_filenames = []
172
+ dir_name = 'test/data/real_xmls/v005/oks/'
173
+ #check dir exists:
174
+ filenames = Dir.entries(dir_name).select{|name| name =~ /.xml/}.sort
175
+ assert filenames.size > 1, "should be at least one file found in #{dir_name}"
176
+ filenames.each do |filename|
177
+ begin
178
+ xml = File.read(File.join(dir_name, filename))
179
+ env = Environment.new_from_eeml(xml)
180
+ passed_filenames << filename
181
+ rescue
182
+ #FileUtils.mv(File.join(dir_name, filename), 'test/real_xmls/v005/questioned')
183
+ raise "While parsing previously acceptable xml in file #{filename} got exception: #{$!.class}: #{$!}." +
184
+ "\nSTART OF XML SNIPPET from #{filename}\n#{xml[0..100]}\nEND OF XML SNIPPET" +
185
+ "\n Prior to error, successfully parsing #{passed_filenames.size} previously-ok xmls"
186
+ end
187
+ end
188
+ puts "passed all %d files" % passed_filenames.size
189
+ end
190
+
191
+ implement "throw detailed exception if missing mandatory node" do
192
+ original_xml = read_xml_file_one
193
+ #we'll test for
194
+ %w(status email).each do |missing_node_name|
195
+ test_xml = remove_first_node_called(missing_node_name, original_xml) #make test document from generic one
196
+ #should throw an exception with the right details
197
+ begin
198
+ Environment.new_from_eeml(test_xml)
199
+ flunk "should've raised an exception"
200
+ rescue MissingNode => e
201
+ assert_equal missing_node_name, e.sought_description, 'should have correct missing node name'
202
+ end
203
+ end
204
+ end
205
+
206
+ test "ignore unexpected attributes on title node" do
207
+ original_xml = read_xml_file_one
208
+ test_xml = original_xml.sub(/<title>/, '<title myattr="unexpected attr here">')
209
+ assert test_xml =~ /myattr/, 'substitution should have placed unexpected "myattr"'
210
+ env = Environment.new_from_eeml(test_xml)
211
+ assert_equal 'title here', env.title
212
+ end
213
+
214
+ test "handle missing optional nodes" do
215
+ original_xml = read_xml_file_one
216
+ #we'll remove the following
217
+ ['title', 'website', ['feed', 'feed_url']].each do |missing_node_name, accessor_name|
218
+ accessor_name = missing_node_name if accessor_name.nil?
219
+ test_xml = remove_first_node_called(missing_node_name, original_xml) #make test document from generic one
220
+ env = Environment.new_from_eeml(test_xml)
221
+ assert_nil env.send((accessor_name).to_sym)
222
+ end
223
+ end
224
+
225
+ # implement "fail if location missing" do
226
+ # original_xml = read_xml_file_one
227
+ # test_xml = remove_first_node_called('location', original_xml)
228
+ # #should throw an exception with the right details
229
+ # begin
230
+ # Environment.new_from_eeml(test_xml)
231
+ # flunk "should've raised an exception for missing location"
232
+ # rescue MissingNode => e
233
+ # assert_equal 'location', e.sought_description, 'should have correct missing node name'
234
+ # end
235
+ # end
236
+
237
+ # --- ----------------------------------------------------
238
+ # --- construction stuff ----------------------------------------------------
239
+ # --- ----------------------------------------------------
240
+
241
+ test "env constructor uses params" do
242
+ env = Environment.new(:identifier => 'whatever', :creator => 'http://www.example.com/ourstudio/',
243
+ :feed_url => 'http://www.example.com/ourstudio/feeds/house.xml')
244
+ assert_equal 'whatever', env.identifier
245
+ assert_equal 'http://www.example.com/ourstudio/', env.creator
246
+ assert_equal 'http://www.example.com/ourstudio/feeds/house.xml', env.feed_url
247
+ end
248
+
249
+ # --- ------------------------------------------------------------
250
+ # --- json stuff ------------------------------------------------------------
251
+ # --- ------------------------------------------------------------
252
+
253
+ test "creating Env obj from a JSON string" do
254
+ env = create_env_from_json_file_one
255
+ assert_not_nil env
256
+ assert_instance_of Environment, env
257
+ end
258
+
259
+
260
+ # --- -------------------------------------------------------------
261
+ # --- csv stuff -------------------------------------------------------------
262
+ # --- -------------------------------------------------------------
263
+
264
+ def create_env_with_datastream_values(values)
265
+ e = Environment.new
266
+ values.each do |v|
267
+ e.add_datastream DataStream.new(:value => v)
268
+ end
269
+ assert_equal values.size, e.datastreams.size
270
+ return e
271
+ end
272
+
273
+ test "update from csv values" do
274
+
275
+ #add a helper method to get an environments datastream values (ignoring min, max) as a simple array
276
+ #TODO: note, can't unload the class before other tests, so be careful with this approach to conveniences.
277
+ class Eeml::Environment
278
+ def values_quick
279
+ datastreams.map {|ds| ds.value}
280
+ end
281
+ end
282
+
283
+ values = ['11.01', '2', 'thirdval', '40'].freeze
284
+ #this env has NO datastreams, so each value will add a ds.
285
+ env = Environment.new
286
+ env.update_datastreams_from_csv_values!(values)
287
+ assert_equal values, env.values_quick
288
+
289
+ #When env has FEWER datastreams than the csv
290
+ #it should update the ones it has, AND add extra datastreams for the excess in the csv
291
+ #TODO: check this assumption:
292
+ # ALL datastreams should take their new value from csv, keeping previous min, max values.
293
+ env = create_env_with_datastream_values(%w(e1 e2 e3))
294
+ env.update_datastreams_from_csv_values!(values)
295
+ assert_equal values, env.values_quick
296
+
297
+ #When env has MORE datastreams than the csv
298
+ #it "should remove extra datastreams if less values are published to the csv feed" do
299
+ env = create_env_with_datastream_values(%w(e1 e2 e3 e4 e5))
300
+ env.update_datastreams_from_csv_values!(values)
301
+ assert_equal values, env.values_quick
302
+
303
+ #WHEN env has SAME number of ds as the csv
304
+ env = create_env_with_datastream_values(%w(e1 e2 e3 e4))
305
+ env.update_datastreams_from_csv_values!(values)
306
+ assert_equal values, env.values_quick
307
+ end
308
+ # --- ----------------------------------------------------------
309
+ # --- output stuff ----------------------------------------------------------
310
+ # --- ----------------------------------------------------------
311
+
312
+ test "should output to xml ok" do
313
+ env = create_env_from_xml_file_one
314
+ xml_output = env.to_eeml
315
+ assert_not_nil xml_output
316
+ #File.open('out.xml', 'w') {|f| f.write xml_output }
317
+
318
+ expected_xml = File.read('test/data/doc_1.xml')
319
+ assert_equal expected_xml, xml_output
320
+ end
321
+
322
+ implement "output - check that 'feed' url is correctly assembled" do
323
+ end
324
+
325
+ test "to_eeml of empty env should work" do
326
+ env = Environment.new
327
+ output_xml = env.to_eeml
328
+ #TODO: Review this 'expected' xml document for when env's empty. I've cheated so far.
329
+ # Perhaps we don't allow serialization at all when basics are absent.
330
+ expected_xml = File.read('test/data/out_empty.xml')
331
+ assert_equal expected_xml, output_xml
332
+ end
333
+
334
+
335
+
336
+ # --- convenience stuff - don't put tests under here ------------------------------
337
+
338
+
339
+
340
+
341
+
342
+ def create_env_from_xml_string(string)
343
+ environment = Environment.new_from_eeml(string)
344
+ assert_not_nil environment
345
+ assert_equal(Environment, environment.class)
346
+ return environment
347
+ end
348
+
349
+ def create_env_from_xml_file_one
350
+ return create_env_from_xml_file('doc_1.xml')
351
+ end
352
+
353
+ def create_env_from_xml_file(test_file)
354
+ filename = 'test/data/' + test_file
355
+ eeml_doc = File.read(filename)
356
+ assert_not_nil eeml_doc, "content of test xml file shouldn't be nil for file: #{filename}"
357
+ return create_env_from_xml_string(eeml_doc)
358
+ end
359
+
360
+ def create_env_from_json_file_one
361
+ return create_env_from_json_file('test/data/doc_1.json')
362
+ end
363
+
364
+ def create_env_from_json_file(filename)
365
+ json = File.read(filename)
366
+ return Environment.new_from_json(json)
367
+ end
368
+
369
+
370
+ end
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'test/libxml_test_helper'
4
+
5
+ # Thought a mock library will be useful for testing out any stuff that
6
+ # does any http-ing, but have no strong feelings about which one to use.
7
+ # Looking at the syntax of each of the libraries, I liked the look of the
8
+ # mocha syntax more, so was thinking to use that when required.
9
+ #
10
+ # require 'flexmock'
11
+ require 'mocha'
12
+
13
+
14
+
15
+ #consider adding custom assertions to clean code up
16
+ #http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit/Assertions.html
17
+
18
+
19
+ # Tiny patch to Test::Unit that lets us write our tests in a slightly
20
+ # more rspec style way. Similar thing is used in jeremymcanally's context
21
+ # gem (http://github.com/jeremymcanally/context/tree/master), but thought
22
+ # could do without the extra dependency
23
+ class Test::Unit::TestCase
24
+ def self.test(name, &block)
25
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
26
+ defined = instance_method(test_name) rescue false
27
+ raise "#{test_name} is already defined in #{self}" if defined
28
+ if block_given?
29
+ define_method(test_name, &block)
30
+ else
31
+ define_method(test_name) do
32
+ flunk "No implementation provided for #{name}"
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.implement(name, &block)
38
+ puts "UNIMPLEMENTED: #{name}"
39
+ end
40
+
41
+ def add_line_numbers(str)
42
+ return nil if str.nil?
43
+ out = ''
44
+ str.split(/\n/).each_with_index do |line, i|
45
+ out << "#{i+1}: #{line}\n"
46
+ end
47
+ out
48
+ end
49
+
50
+ end
51
+
52
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
53
+
54
+ require 'eeml'
55
+
56
+ include Eeml
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+ require 'lib/eeml/libxml_eeml_parser_v005.rb'
3
+ class TestLibXMLEemlParserV005 < Test::Unit::TestCase
4
+ test "missing node ex raises detailed exception" do
5
+ p = LibXMLEemlParserV005.new
6
+ #p.make_environment_from_xml('bad')
7
+ end
8
+
9
+ end
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class TestLibXMLTestHelper < Test::Unit::TestCase
4
+ include LibXMLTestHelper
5
+ #testing the convenience methods!
6
+ def test_remove_nodes
7
+ input_xml=<<-END
8
+ <list>
9
+ <food type='dairy' name='milk'>
10
+ <cost>32</cost>
11
+ </food>
12
+ <thing2></thing2>
13
+ <food type='other' name='food2'>
14
+ </food>
15
+ </list>
16
+ END
17
+
18
+ #test remove only first node
19
+ expected_xml=<<-END
20
+ <list>
21
+ <thing2></thing2>
22
+ <food type='other' name='food2'>
23
+ </food>
24
+ </list>
25
+ END
26
+ assert_equal expected_xml.gsub(/\n\W*/, ''), remove_first_node_called('food', input_xml).gsub(/\n\W*/, '')
27
+
28
+ #test remove all nodes
29
+ expected_xml=<<-END
30
+ <list>
31
+ <thing2></thing2>
32
+ </list>
33
+ END
34
+ assert_equal expected_xml.gsub(/\n\W*/, ''), remove_nodes_called('food', input_xml).gsub(/\n\W*/, '')
35
+
36
+ end
37
+
38
+
39
+
40
+
41
+ #testing the convenience methods!
42
+ def test_replace_nodes
43
+ input_xml=<<-END
44
+ <list>
45
+ <food type='dairy' name='milk'>
46
+ <cost>32</cost>
47
+ </food>
48
+ <thing2></thing2>
49
+ <food type='other' name='food2'>
50
+ </food>
51
+ </list>
52
+ END
53
+
54
+
55
+ #test replace only first node
56
+ expected_xml=<<-END
57
+ <list>
58
+ <snack name='lions'></snack><snack name='tigers'></snack>
59
+ <thing2></thing2>
60
+ <food type='other' name='food2'>
61
+ </food>
62
+ </list>
63
+ END
64
+ replacement_string = "<snack name='lions'></snack><snack name='tigers'></snack>"
65
+
66
+ output = replace_nodes_called('food', input_xml, replacement_string, true)
67
+ assert_equal expected_xml.gsub(/\n\W*/, ''), output.gsub(/\n\W*/, '')
68
+
69
+ output = replace_first_node_called('food', input_xml, replacement_string)
70
+ assert_equal expected_xml.gsub(/\n\W*/, ''), output.gsub(/\n\W*/, '')
71
+
72
+ #test replace all matching nodes
73
+ expected_xml=<<-END
74
+ <list>
75
+ <snack name='lions'></snack><snack name='tigers'></snack>
76
+ <thing2></thing2>
77
+ <snack name='lions'></snack><snack name='tigers'></snack>
78
+ </list>
79
+ END
80
+ output = replace_nodes_called('food', input_xml, replacement_string, false)
81
+ assert_equal expected_xml.gsub(/\n\W*/, ''), output.gsub(/\n\W*/, '')
82
+
83
+ end
84
+ end
85
+
data.tar.gz.sig ADDED
@@ -0,0 +1 @@
1
+ ������^T Jg����(?�P�e�����1�U�F�-͎t?21��ϫ��!�:�t9��8��~[S޽$�Y8e����e D�A�b���c� �P���ѻl�Ø>����ה.:�����v҂)���d-s���9 #IW