kronk 1.5.4 → 1.6.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.
- data/History.rdoc +14 -0
- data/Manifest.txt +8 -2
- data/README.rdoc +6 -0
- data/TODO.rdoc +12 -0
- data/lib/kronk.rb +6 -2
- data/lib/kronk/cmd.rb +18 -2
- data/lib/kronk/constants.rb +1 -0
- data/lib/kronk/data_renderer.rb +95 -22
- data/lib/kronk/diff.rb +18 -64
- data/lib/kronk/diff/ascii_format.rb +11 -0
- data/lib/kronk/diff/color_format.rb +14 -2
- data/lib/kronk/diff/output.rb +155 -0
- data/lib/kronk/path.rb +48 -153
- data/lib/kronk/path/matcher.rb +189 -0
- data/lib/kronk/path/path_match.rb +74 -0
- data/lib/kronk/path/transaction.rb +157 -47
- data/lib/kronk/player/benchmark.rb +2 -1
- data/lib/kronk/player/suite.rb +8 -0
- data/lib/kronk/response.rb +7 -6
- data/test/test_cmd.rb +29 -8
- data/test/test_data_string.rb +58 -0
- data/test/test_diff.rb +137 -36
- data/test/test_helper.rb +2 -0
- data/test/test_kronk.rb +19 -3
- data/test/test_path.rb +87 -170
- data/test/test_path_match.rb +60 -0
- data/test/test_path_matcher.rb +329 -0
- data/test/test_response.rb +10 -10
- data/test/test_transaction.rb +132 -3
- metadata +82 -75
@@ -118,7 +118,7 @@ class Kronk
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
|
-
perc_list.each{|
|
121
|
+
perc_list.each{|l| @percentages[l] ||= self.slowest }
|
122
122
|
@percentages[100] = self.slowest
|
123
123
|
@percentages
|
124
124
|
end
|
@@ -252,6 +252,7 @@ end
|
|
252
252
|
|
253
253
|
if Float.instance_method(:round).arity == 0
|
254
254
|
class Float
|
255
|
+
undef round
|
255
256
|
def round ndigits=0
|
256
257
|
num, dec = self.to_s.split(".")
|
257
258
|
num = "#{num}.#{dec[0,ndigits]}".sub(/\.$/, "")
|
data/lib/kronk/player/suite.rb
CHANGED
@@ -25,6 +25,14 @@ class Kronk
|
|
25
25
|
[status, time, text]
|
26
26
|
|
27
27
|
elsif kronk.response
|
28
|
+
begin
|
29
|
+
# Make sure response is parsable
|
30
|
+
kronk.response.stringify kronk.options
|
31
|
+
rescue => e
|
32
|
+
error e, kronk, mutex
|
33
|
+
return
|
34
|
+
end if kronk.response.success?
|
35
|
+
|
28
36
|
status = "F" if !kronk.response.success?
|
29
37
|
text = resp_text kronk if status == "F"
|
30
38
|
[status, kronk.response.time, text]
|
data/lib/kronk/response.rb
CHANGED
@@ -28,8 +28,6 @@ class Kronk
|
|
28
28
|
# Read http response from a file and return a HTTPResponse instance.
|
29
29
|
|
30
30
|
def self.read_file path
|
31
|
-
Kronk::Cmd.verbose "Reading file: #{path}\n"
|
32
|
-
|
33
31
|
file = File.open(path, "rb")
|
34
32
|
resp = new file
|
35
33
|
resp.uri = path
|
@@ -60,7 +58,8 @@ class Kronk
|
|
60
58
|
@_res, debug_io = request_from_io(io)
|
61
59
|
end
|
62
60
|
|
63
|
-
@headers = @_res.to_hash
|
61
|
+
@headers = @_res.to_hash.dup
|
62
|
+
@headers.keys.each{|h| @headers[h] = @headers[h].join(", ")}
|
64
63
|
|
65
64
|
@encoding = "utf-8" unless @_res["Content-Type"]
|
66
65
|
c_type = [*@headers["content-type"]].find{|ct| ct =~ ENCODING_MATCHER}
|
@@ -182,7 +181,7 @@ class Kronk
|
|
182
181
|
# Returns the parsed header hash.
|
183
182
|
|
184
183
|
def parsed_header include_headers=true
|
185
|
-
headers = @
|
184
|
+
headers = @headers.dup
|
186
185
|
|
187
186
|
case include_headers
|
188
187
|
when nil, false
|
@@ -277,7 +276,9 @@ class Kronk
|
|
277
276
|
end
|
278
277
|
|
279
278
|
if options[:with_headers]
|
280
|
-
|
279
|
+
header_data = parsed_header(options[:with_headers])
|
280
|
+
data &&= [header_data, data]
|
281
|
+
data ||= header_data
|
281
282
|
end
|
282
283
|
|
283
284
|
Path::Transaction.run data, options do |t|
|
@@ -300,7 +301,7 @@ class Kronk
|
|
300
301
|
# :with_headers:: Boolean/String/Array - defines which headers to include
|
301
302
|
|
302
303
|
def stringify options={}
|
303
|
-
if !options[:raw] && (options[:parser] || @parser)
|
304
|
+
if !options[:raw] && (options[:parser] || @parser || options[:no_body])
|
304
305
|
data = selective_data options
|
305
306
|
Diff.ordered_data_string data, options[:struct]
|
306
307
|
else
|
data/test/test_cmd.rb
CHANGED
@@ -145,6 +145,25 @@ class TestCmd < Test::Unit::TestCase
|
|
145
145
|
end
|
146
146
|
|
147
147
|
|
148
|
+
def test_parse_args_context
|
149
|
+
with_config do
|
150
|
+
opts = Kronk::Cmd.parse_args %w{uri --context}
|
151
|
+
assert_equal 3, opts[:context]
|
152
|
+
assert_equal nil, Kronk.config[:context]
|
153
|
+
|
154
|
+
Kronk.config[:context] = 4
|
155
|
+
opts = Kronk::Cmd.parse_args %w{uri --context}
|
156
|
+
assert_equal 4, opts[:context]
|
157
|
+
|
158
|
+
opts = Kronk::Cmd.parse_args %w{uri --context 5}
|
159
|
+
assert_equal 5, opts[:context]
|
160
|
+
|
161
|
+
opts = Kronk::Cmd.parse_args %w{uri --full}
|
162
|
+
assert_equal false, opts[:context]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
148
167
|
def test_parse_args_completion
|
149
168
|
with_config Hash.new do
|
150
169
|
file = File.join(File.dirname(__FILE__), "../script/kronk_completion")
|
@@ -168,18 +187,19 @@ class TestCmd < Test::Unit::TestCase
|
|
168
187
|
|
169
188
|
def test_parse_args_kronk_configs
|
170
189
|
with_config Hash.new do
|
171
|
-
Kronk::Cmd.parse_args %w{uri -q -l --no-opts -V -t 1234 --ruby}
|
190
|
+
Kronk::Cmd.parse_args %w{uri -q -l --no-opts -V -t 1234 --ruby --paths}
|
172
191
|
assert Kronk.config[:brief]
|
173
192
|
assert Kronk.config[:show_lines]
|
174
193
|
assert Kronk.config[:no_uri_options]
|
175
194
|
assert Kronk.config[:verbose]
|
195
|
+
assert Kronk.config[:render_paths]
|
176
196
|
assert_equal 'ruby', Kronk.config[:render_lang]
|
177
197
|
assert_equal 1234, Kronk.config[:timeout]
|
178
198
|
end
|
179
199
|
end
|
180
200
|
|
181
201
|
|
182
|
-
def
|
202
|
+
def test_parse_args_http_headers
|
183
203
|
opts = Kronk::Cmd.parse_args %w{uri -i FOO -i BAR -iTWO,PART}
|
184
204
|
assert_equal %w{FOO BAR TWO PART}, opts[:with_headers]
|
185
205
|
assert_equal false, opts[:no_body]
|
@@ -380,25 +400,26 @@ class TestCmd < Test::Unit::TestCase
|
|
380
400
|
|
381
401
|
|
382
402
|
def test_run_compare
|
383
|
-
Kronk.expects(:load_config)
|
384
|
-
expect_compare_output mock_200_response
|
385
|
-
|
386
403
|
file = File.join(File.dirname(__FILE__), "mocks/200_response.txt")
|
387
404
|
file = File.expand_path file
|
388
405
|
|
406
|
+
Kronk.expects(:load_config)
|
407
|
+
expect_compare_output mock_200_response, :labels => [file, file]
|
408
|
+
|
389
409
|
Kronk::Cmd.run [file, file]
|
390
410
|
end
|
391
411
|
|
392
412
|
|
393
413
|
def test_run_compare_diff
|
394
|
-
Kronk.expects(:load_config)
|
395
|
-
expect_compare_output mock_200_response, mock_302_response
|
396
|
-
|
397
414
|
file1 = File.join(File.dirname(__FILE__), "mocks/200_response.txt")
|
398
415
|
file2 = File.join(File.dirname(__FILE__), "mocks/302_response.txt")
|
399
416
|
file1 = File.expand_path file1
|
400
417
|
file2 = File.expand_path file2
|
401
418
|
|
419
|
+
Kronk.expects(:load_config)
|
420
|
+
expect_compare_output mock_200_response, mock_302_response,
|
421
|
+
:labels => [file1, file2]
|
422
|
+
|
402
423
|
assert_exit 1 do
|
403
424
|
Kronk::Cmd.run [file1, file2]
|
404
425
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class TestDataString < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@dstr = Kronk::DataString.new "foobar", "data0"
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def test_new
|
11
|
+
expected_meta = ["data0"] * @dstr.length
|
12
|
+
assert_equal expected_meta, @dstr.meta
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def test_append
|
17
|
+
@dstr.append "\nthingz", "data1"
|
18
|
+
expected_meta = (["data0"] * 6) + (["data1"] * 7)
|
19
|
+
assert_equal expected_meta, @dstr.meta
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def test_insert
|
24
|
+
@dstr << "\nthingz"
|
25
|
+
expected_meta = ["data0"] * 13
|
26
|
+
assert_equal expected_meta, @dstr.meta
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def test_select
|
31
|
+
@dstr.append "\nthingz", "data1"
|
32
|
+
new_dstr = @dstr[4..9]
|
33
|
+
expected_meta = (["data0"] * 2) + (["data1"] * 4)
|
34
|
+
assert_equal expected_meta, new_dstr.meta
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def test_split
|
39
|
+
@dstr.append "\nthingz", "data1"
|
40
|
+
arr = @dstr.split
|
41
|
+
|
42
|
+
expected = ["data0"] * 6
|
43
|
+
assert_equal expected, arr.first.meta
|
44
|
+
|
45
|
+
expected = ["data1"] * 6
|
46
|
+
assert_equal expected, arr.last.meta
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def test_split_chars
|
51
|
+
@dstr.append "\nthingz", "data1"
|
52
|
+
arr = @dstr.split ''
|
53
|
+
|
54
|
+
arr.each_with_index do |dstr, i|
|
55
|
+
assert_equal [@dstr.meta[i]], dstr.meta
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/test/test_diff.rb
CHANGED
@@ -174,12 +174,10 @@ STR
|
|
174
174
|
Kronk::Diff.ordered_data_string(mock_data, true)
|
175
175
|
end
|
176
176
|
|
177
|
-
|
178
|
-
|
179
|
-
with_config :render_lang => 'ruby' do
|
180
|
-
expected = <<STR
|
177
|
+
def test_ordered_data_string_json
|
178
|
+
expected = <<STR
|
181
179
|
{
|
182
|
-
"acks"
|
180
|
+
"acks": [
|
183
181
|
[
|
184
182
|
56,
|
185
183
|
78
|
@@ -189,7 +187,7 @@ STR
|
|
189
187
|
"34"
|
190
188
|
]
|
191
189
|
],
|
192
|
-
"root"
|
190
|
+
"root": [
|
193
191
|
[
|
194
192
|
"B1",
|
195
193
|
"B2"
|
@@ -207,11 +205,11 @@ STR
|
|
207
205
|
]
|
208
206
|
],
|
209
207
|
{
|
210
|
-
:tests
|
208
|
+
":tests": [
|
211
209
|
"D3a",
|
212
210
|
"D3b"
|
213
211
|
],
|
214
|
-
"test"
|
212
|
+
"test": [
|
215
213
|
[
|
216
214
|
"D1a\\nContent goes here",
|
217
215
|
"D1b"
|
@@ -220,13 +218,13 @@ STR
|
|
220
218
|
]
|
221
219
|
}
|
222
220
|
],
|
223
|
-
"subs"
|
221
|
+
"subs": [
|
224
222
|
"a",
|
225
223
|
"b"
|
226
224
|
],
|
227
|
-
"tests"
|
228
|
-
:foo
|
229
|
-
"test"
|
225
|
+
"tests": {
|
226
|
+
":foo": ":bar",
|
227
|
+
"test": [
|
230
228
|
[
|
231
229
|
1,
|
232
230
|
2
|
@@ -235,6 +233,40 @@ STR
|
|
235
233
|
]
|
236
234
|
}
|
237
235
|
}
|
236
|
+
STR
|
237
|
+
|
238
|
+
assert_equal expected.strip, Kronk::Diff.ordered_data_string(mock_data)
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
def test_ordered_data_string_ruby_paths
|
243
|
+
with_config :render_lang => 'ruby', :render_paths => true do
|
244
|
+
expected = <<STR
|
245
|
+
{
|
246
|
+
"/acks/0/0" => 56,
|
247
|
+
"/acks/0/1" => 78,
|
248
|
+
"/acks/1/0" => "12",
|
249
|
+
"/acks/1/1" => "34",
|
250
|
+
"/root/0/0" => "B1",
|
251
|
+
"/root/0/1" => "B2",
|
252
|
+
"/root/1/0" => "A1",
|
253
|
+
"/root/1/1" => "A2",
|
254
|
+
"/root/2/0" => "C1",
|
255
|
+
"/root/2/1" => "C2",
|
256
|
+
"/root/2/2/0" => "C3a",
|
257
|
+
"/root/2/2/1" => "C3b",
|
258
|
+
"/root/3/test/0/0" => "D1a\\nContent goes here",
|
259
|
+
"/root/3/test/0/1" => "D1b",
|
260
|
+
"/root/3/test/1" => "D2",
|
261
|
+
"/root/3/tests/0" => "D3a",
|
262
|
+
"/root/3/tests/1" => "D3b",
|
263
|
+
"/subs/0" => "a",
|
264
|
+
"/subs/1" => "b",
|
265
|
+
"/tests/foo" => :bar,
|
266
|
+
"/tests/test/0/0" => 1,
|
267
|
+
"/tests/test/0/1" => 2,
|
268
|
+
"/tests/test/1" => 2.123
|
269
|
+
}
|
238
270
|
STR
|
239
271
|
|
240
272
|
assert_equal expected.strip, Kronk::Diff.ordered_data_string(mock_data)
|
@@ -481,30 +513,43 @@ STR
|
|
481
513
|
|
482
514
|
|
483
515
|
def test_formatted_lines
|
484
|
-
|
516
|
+
@diff.output.show_lines = true
|
517
|
+
output = @diff.formatted
|
485
518
|
assert_equal diff_302_301_str_lines, output
|
486
519
|
end
|
487
520
|
|
488
521
|
|
489
522
|
def test_formatted_color
|
490
|
-
|
491
|
-
@diff.formatted(:formatter => Kronk::Diff::ColorFormat)
|
523
|
+
@diff.output.format = Kronk::Diff::ColorFormat
|
492
524
|
|
493
|
-
|
494
|
-
|
525
|
+
assert_equal diff_302_301_color,
|
526
|
+
@diff.formatted
|
495
527
|
end
|
496
528
|
|
497
529
|
|
498
530
|
def test_formatted_join_char
|
499
531
|
expected = diff_302_301_str.gsub(/\n/, "\r\n")
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
532
|
+
@diff.output.format = Kronk::Diff::AsciiFormat
|
533
|
+
@diff.output.join_ch = "\r\n"
|
534
|
+
|
535
|
+
assert_equal expected, @diff.formatted
|
504
536
|
end
|
505
537
|
|
506
538
|
|
507
|
-
|
539
|
+
def test_formatted_context
|
540
|
+
resp1 = Kronk::Response.read_file "test/mocks/200_response_diff.json"
|
541
|
+
resp2 = Kronk::Response.read_file "test/mocks/200_response.json"
|
542
|
+
@diff = Kronk::Diff.new resp1.stringify, resp2.stringify
|
543
|
+
|
544
|
+
@diff.output.format = Kronk::Diff::ColorFormat
|
545
|
+
@diff.output.context = 3
|
546
|
+
@diff.output.labels = [resp1.uri, resp2.uri]
|
547
|
+
|
548
|
+
assert_equal diff_json_color, @diff.formatted
|
549
|
+
end
|
550
|
+
|
551
|
+
|
552
|
+
class CustomFormat < Kronk::Diff::AsciiFormat
|
508
553
|
def self.added str
|
509
554
|
">>>301>>> #{str}"
|
510
555
|
end
|
@@ -519,9 +564,10 @@ STR
|
|
519
564
|
end
|
520
565
|
|
521
566
|
def test_formatted_custom
|
522
|
-
|
523
|
-
|
524
|
-
expected =
|
567
|
+
@diff.output.format = CustomFormat
|
568
|
+
str_diff = @diff.formatted
|
569
|
+
expected = diff_302_301_str.gsub(/^\+ /, ">>>301>>> ")
|
570
|
+
expected = expected.gsub(/^\- /, "<<<302<<< ")
|
525
571
|
expected = expected.gsub(/^\s\s/, "")
|
526
572
|
|
527
573
|
assert_equal expected, str_diff
|
@@ -529,6 +575,55 @@ STR
|
|
529
575
|
|
530
576
|
private
|
531
577
|
|
578
|
+
def diff_json_color
|
579
|
+
(<<-STR
|
580
|
+
\e[1;33m--- test/mocks/200_response_diff.json
|
581
|
+
+++ test/mocks/200_response.json\e[0m
|
582
|
+
\e[1;35m@@ -6,7 +6,7 @@\e[0m business/description
|
583
|
+
"additional_urls": [
|
584
|
+
{
|
585
|
+
"destination": "http://example.com",
|
586
|
+
\e[1;31m- "url_click": "http://google.com"\e[0m
|
587
|
+
\e[1;32m+ "url_click": "http://example.com"\e[0m
|
588
|
+
}
|
589
|
+
],
|
590
|
+
"general_info": "<p>A Paint Your Own Pottery Studios..</p>",
|
591
|
+
\e[1;35m@@ -15,11 +15,12 @@\e[0m business/description
|
592
|
+
"slogan": "<p>Pottery YOU dress up</p>"
|
593
|
+
},
|
594
|
+
"distance": 0.0,
|
595
|
+
\e[1;31m- "has_detail_page": false,\e[0m
|
596
|
+
\e[1;32m+ "has_detail_page": true,\e[0m
|
597
|
+
"headings": [
|
598
|
+
"Pottery"
|
599
|
+
],
|
600
|
+
\e[1;31m- "id": 1234,\e[0m
|
601
|
+
\e[1;32m+ "id": "1234",\e[0m
|
602
|
+
\e[1;32m+ "impression_id": "mock_iid",\e[0m
|
603
|
+
"improvable": true,
|
604
|
+
"latitude": 42.882561,
|
605
|
+
"listing_id": "1234",
|
606
|
+
\e[1;35m@@ -34,12 +35,12 @@\e[0m business
|
607
|
+
"rating_count": 0,
|
608
|
+
"red_listing": false,
|
609
|
+
"state": "MI",
|
610
|
+
\e[1;31m- "website": "http://google.com",\e[0m
|
611
|
+
\e[1;32m+ "website": "http://example.com",\e[0m
|
612
|
+
"year_established": "1996",
|
613
|
+
"zip": "49418"
|
614
|
+
},
|
615
|
+
\e[1;31m- "original": {\e[0m
|
616
|
+
\e[1;32m+ "original_request": {\e[0m
|
617
|
+
"id": "1234"
|
618
|
+
},
|
619
|
+
\e[1;31m- "request_id": "foobar"\e[0m
|
620
|
+
\e[1;32m+ "request_id": "mock_rid"\e[0m
|
621
|
+
}
|
622
|
+
STR
|
623
|
+
).strip
|
624
|
+
end
|
625
|
+
|
626
|
+
|
532
627
|
def diff_302_301
|
533
628
|
[[["HTTP/1.1 302 Found", "Location: http://igoogle.com/"],
|
534
629
|
["HTTP/1.1 301 Moved Permanently", "Location: http://www.google.com/"]],
|
@@ -570,6 +665,8 @@ STR
|
|
570
665
|
|
571
666
|
def diff_302_301_str
|
572
667
|
str = <<STR
|
668
|
+
--- left
|
669
|
+
+++ right
|
573
670
|
- HTTP/1.1 302 Found
|
574
671
|
- Location: http://igoogle.com/
|
575
672
|
+ HTTP/1.1 301 Moved Permanently
|
@@ -599,6 +696,8 @@ STR
|
|
599
696
|
|
600
697
|
def diff_302_301_str_lines
|
601
698
|
str = <<STR
|
699
|
+
--- left
|
700
|
+
+++ right
|
602
701
|
1| - HTTP/1.1 302 Found
|
603
702
|
2| - Location: http://igoogle.com/
|
604
703
|
| 1 + HTTP/1.1 301 Moved Permanently
|
@@ -629,27 +728,29 @@ STR
|
|
629
728
|
|
630
729
|
def diff_302_301_color
|
631
730
|
str = <<STR
|
632
|
-
\
|
633
|
-
\033[
|
634
|
-
\033[
|
635
|
-
\033[
|
731
|
+
\e[1;33m--- left
|
732
|
+
+++ right\033[0m
|
733
|
+
\033[1;31m- HTTP/1.1 302 Found\033[0m
|
734
|
+
\033[1;31m- Location: http://igoogle.com/\033[0m
|
735
|
+
\033[1;32m+ HTTP/1.1 301 Moved Permanently\033[0m
|
736
|
+
\033[1;32m+ Location: http://www.google.com/\033[0m
|
636
737
|
Content-Type: text/html; charset=UTF-8
|
637
738
|
Date: Fri, 26 Nov 2010 16:14:45 GMT
|
638
739
|
Expires: Sun, 26 Dec 2010 16:14:45 GMT
|
639
740
|
Cache-Control: public, max-age=2592000
|
640
741
|
Server: gws
|
641
|
-
\033[31m- Content-Length: 260\033[0m
|
642
|
-
\033[32m+ Content-Length: 219\033[0m
|
742
|
+
\033[1;31m- Content-Length: 260\033[0m
|
743
|
+
\033[1;32m+ Content-Length: 219\033[0m
|
643
744
|
X-XSS-Protection: 1; mode=block
|
644
745
|
|
645
746
|
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
|
646
|
-
\033[31m- <TITLE>302 Found</TITLE></HEAD><BODY>\033[0m
|
647
|
-
\033[31m- <H1>302 Found</H1>\033[0m
|
648
|
-
\033[32m+ <TITLE>301 Moved</TITLE></HEAD><BODY>\033[0m
|
649
|
-
\033[32m+ <H1>301 Moved</H1>\033[0m
|
747
|
+
\033[1;31m- <TITLE>302 Found</TITLE></HEAD><BODY>\033[0m
|
748
|
+
\033[1;31m- <H1>302 Found</H1>\033[0m
|
749
|
+
\033[1;32m+ <TITLE>301 Moved</TITLE></HEAD><BODY>\033[0m
|
750
|
+
\033[1;32m+ <H1>301 Moved</H1>\033[0m
|
650
751
|
The document has moved
|
651
752
|
<A HREF="http://www.google.com/">here</A>.
|
652
|
-
\033[31m- <A HREF="http://igoogle.com/">here</A>.\033[0m
|
753
|
+
\033[1;31m- <A HREF="http://igoogle.com/">here</A>.\033[0m
|
653
754
|
</BODY></HTML>
|
654
755
|
STR
|
655
756
|
str.strip
|