sass 3.7.4 → 4.0.0.alpha.1

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.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -0,0 +1,12 @@
1
+ $preconst: hello;
2
+
3
+ @mixin premixin {pre-mixin: here}
4
+
5
+ @import "importee.sass", "scss_importee", "basic.sass", "basic.css", "../results/complex.css";
6
+ @import "part\
7
+ ial.sass";
8
+
9
+ nonimported {
10
+ myconst: $preconst;
11
+ otherconst: $postconst;
12
+ @include postmixin; }
@@ -0,0 +1 @@
1
+ scss {imported: yes}
@@ -0,0 +1 @@
1
+ @import "single_import_loop"
@@ -0,0 +1 @@
1
+ @import "../subdir/import_up2.scss";
@@ -0,0 +1 @@
1
+ @import "../subdir/import_up3.scss";
@@ -0,0 +1,2 @@
1
+ #nested
2
+ :relative true
@@ -0,0 +1,6 @@
1
+ @import nested_subdir/nested_partial.sass
2
+
3
+ #subdir
4
+ :font
5
+ :size 20px
6
+ :weight bold
@@ -0,0 +1,11 @@
1
+ b
2
+ :foo 0.5 * 10px
3
+ :bar 10zzz * 12px / 5zzz
4
+ :baz percentage(12.0px / 18px)
5
+ :many-units 10.0zzz / 3yyy * 12px / 5zzz * 3yyy / 3px * 4em
6
+ :mm 5mm + 1cm
7
+ :pc 1pc + 12pt
8
+ :pt 72pt - 2in
9
+ :inches 1in + 2.54cm
10
+ :more-inches 1in + ((72pt * 2in) + (36pt * 1in)) / 2.54cm
11
+ :mixed (1 + (1em * 6px / 3in)) * 4in / 2em
@@ -0,0 +1,3 @@
1
+ @warn "In the main file"
2
+ @import warn_imported.sass
3
+ +emits-a-warning
@@ -0,0 +1,4 @@
1
+ @warn "Imported"
2
+
3
+ =emits-a-warning
4
+ @warn "In an imported mixin"
@@ -0,0 +1,8 @@
1
+ test_dir = File.dirname(__FILE__)
2
+ $:.unshift test_dir unless $:.include?(test_dir)
3
+
4
+ class MiniTest::Test
5
+ def absolutize(file)
6
+ File.expand_path("#{File.dirname(__FILE__)}/#{file}")
7
+ end
8
+ end
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require File.dirname(__FILE__) + '/../../test_helper'
4
+
5
+ unless Sass::Util.ruby1_8?
6
+ class MultibyteStringScannerTest < MiniTest::Test
7
+ def setup
8
+ @scanner = Sass::Util::MultibyteStringScanner.new("cölorfül")
9
+ end
10
+
11
+ def test_initial
12
+ assert_scanner_state 0, 0, nil, nil
13
+ end
14
+
15
+ def test_check
16
+ assert_equal 'cö', @scanner.check(/../)
17
+ assert_scanner_state 0, 0, 2, 3
18
+ assert_equal 0, @scanner.pos
19
+ assert_equal 0, @scanner.pos
20
+ assert_equal 2, @scanner.matched_size
21
+ assert_equal 3, @scanner.byte_matched_size
22
+ end
23
+
24
+ def test_check_until
25
+ assert_equal 'cölorfü', @scanner.check_until(/f./)
26
+ assert_scanner_state 0, 0, 2, 3
27
+ end
28
+
29
+ def test_getch
30
+ assert_equal 'c', @scanner.getch
31
+ assert_equal 'ö', @scanner.getch
32
+ assert_scanner_state 2, 3, 1, 2
33
+ end
34
+
35
+ def test_match?
36
+ assert_equal 2, @scanner.match?(/../)
37
+ assert_scanner_state 0, 0, 2, 3
38
+ end
39
+
40
+ def test_peek
41
+ assert_equal 'cö', @scanner.peek(2)
42
+ assert_scanner_state 0, 0, nil, nil
43
+ end
44
+
45
+ def test_rest_size
46
+ assert_equal 'cö', @scanner.scan(/../)
47
+ assert_equal 6, @scanner.rest_size
48
+ end
49
+
50
+ def test_scan
51
+ assert_equal 'cö', @scanner.scan(/../)
52
+ assert_scanner_state 2, 3, 2, 3
53
+ end
54
+
55
+ def test_scan_until
56
+ assert_equal 'cölorfü', @scanner.scan_until(/f./)
57
+ assert_scanner_state 7, 9, 2, 3
58
+ end
59
+
60
+ def test_skip
61
+ assert_equal 2, @scanner.skip(/../)
62
+ assert_scanner_state 2, 3, 2, 3
63
+ end
64
+
65
+ def test_skip_until
66
+ assert_equal 7, @scanner.skip_until(/f./)
67
+ assert_scanner_state 7, 9, 2, 3
68
+ end
69
+
70
+ def test_set_pos
71
+ @scanner.pos = 7
72
+ assert_scanner_state 7, 9, nil, nil
73
+ @scanner.pos = 6
74
+ assert_scanner_state 6, 7, nil, nil
75
+ @scanner.pos = 1
76
+ assert_scanner_state 1, 1, nil, nil
77
+ end
78
+
79
+ def test_reset
80
+ @scanner.scan(/../)
81
+ @scanner.reset
82
+ assert_scanner_state 0, 0, nil, nil
83
+ end
84
+
85
+ def test_scan_full
86
+ assert_equal 'cö', @scanner.scan_full(/../, true, true)
87
+ assert_scanner_state 2, 3, 2, 3
88
+
89
+ @scanner.reset
90
+ assert_equal 'cö', @scanner.scan_full(/../, false, true)
91
+ assert_scanner_state 0, 0, 2, 3
92
+
93
+ @scanner.reset
94
+ assert_nil @scanner.scan_full(/../, true, false)
95
+ assert_scanner_state 2, 3, 2, 3
96
+
97
+ @scanner.reset
98
+ assert_nil @scanner.scan_full(/../, false, false)
99
+ assert_scanner_state 0, 0, 2, 3
100
+ end
101
+
102
+ def test_search_full
103
+ assert_equal 'cölorfü', @scanner.search_full(/f./, true, true)
104
+ assert_scanner_state 7, 9, 2, 3
105
+
106
+ @scanner.reset
107
+ assert_equal 'cölorfü', @scanner.search_full(/f./, false, true)
108
+ assert_scanner_state 0, 0, 2, 3
109
+
110
+ @scanner.reset
111
+ assert_nil @scanner.search_full(/f./, true, false)
112
+ assert_scanner_state 7, 9, 2, 3
113
+
114
+ @scanner.reset
115
+ assert_nil @scanner.search_full(/f./, false, false)
116
+ assert_scanner_state 0, 0, 2, 3
117
+ end
118
+
119
+ def test_set_string
120
+ @scanner.scan(/../)
121
+ @scanner.string = 'föóbâr'
122
+ assert_scanner_state 0, 0, nil, nil
123
+ end
124
+
125
+ def test_terminate
126
+ @scanner.scan(/../)
127
+ @scanner.terminate
128
+ assert_scanner_state 8, 10, nil, nil
129
+ end
130
+
131
+ def test_unscan
132
+ @scanner.scan(/../)
133
+ @scanner.scan_until(/f./)
134
+ @scanner.unscan
135
+ assert_scanner_state 2, 3, nil, nil
136
+ end
137
+
138
+ private
139
+
140
+ def assert_scanner_state(pos, byte_pos, matched_size, byte_matched_size)
141
+ assert_equal pos, @scanner.pos, 'pos'
142
+ assert_equal byte_pos, @scanner.byte_pos, 'byte_pos'
143
+ assert_equal matched_size, @scanner.matched_size, 'matched_size'
144
+ assert_equal byte_matched_size, @scanner.byte_matched_size, 'byte_matched_size'
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../test_helper'
3
+ require 'sass/util/normalized_map'
4
+
5
+ class NormalizedMapTest < MiniTest::Test
6
+ extend PublicApiLinter
7
+
8
+ lint_api Hash, Sass::Util::NormalizedMap
9
+
10
+ def lint_instance
11
+ Sass::Util::NormalizedMap.new
12
+ end
13
+
14
+ def test_normalized_map_errors_unless_explicitly_implemented
15
+ assert Sass.tests_running
16
+ assert_raise_message(ArgumentError, "The method invert must be implemented explicitly") do
17
+ Sass::Util::NormalizedMap.new.invert
18
+ end
19
+ end
20
+
21
+ def test_normalized_map_does_not_error_when_released
22
+ Sass.tests_running = false
23
+ assert_equal({}, Sass::Util::NormalizedMap.new.invert)
24
+ ensure
25
+ Sass.tests_running = true
26
+ end
27
+
28
+ def test_basic_lifecycle
29
+ m = Sass::Util::NormalizedMap.new
30
+ m["a-b"] = 1
31
+ assert_equal ["a_b"], m.keys
32
+ assert_equal 1, m["a_b"]
33
+ assert_equal 1, m["a-b"]
34
+ assert m.has_key?("a_b")
35
+ assert m.has_key?("a-b")
36
+ assert_equal({"a-b" => 1}, m.as_stored)
37
+ assert_equal 1, m.delete("a-b")
38
+ assert !m.has_key?("a-b")
39
+ m["a_b"] = 2
40
+ assert_equal({"a_b" => 2}, m.as_stored)
41
+ end
42
+
43
+ def test_dup
44
+ m = Sass::Util::NormalizedMap.new
45
+ m["a-b"] = 1
46
+ m2 = m.dup
47
+ m.delete("a-b")
48
+ assert !m.has_key?("a-b")
49
+ assert m2.has_key?("a-b")
50
+ end
51
+ end
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../test_helper'
3
+
4
+ class SubsetMapTest < MiniTest::Test
5
+ def setup
6
+ @ssm = Sass::Util::SubsetMap.new
7
+ @ssm[Set[1, 2]] = "Foo"
8
+ @ssm[Set["fizz", "fazz"]] = "Bar"
9
+
10
+ @ssm[Set[:foo, :bar]] = "Baz"
11
+ @ssm[Set[:foo, :bar, :baz]] = "Bang"
12
+
13
+ @ssm[Set[:bip, :bop, :blip]] = "Qux"
14
+ @ssm[Set[:bip, :bop]] = "Thram"
15
+ end
16
+
17
+ def test_equal_keys
18
+ assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2])
19
+ assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz"])
20
+ end
21
+
22
+ def test_subset_keys
23
+ assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2, "fuzz"])
24
+ assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz", 3])
25
+ end
26
+
27
+ def test_superset_keys
28
+ assert_equal [], @ssm.get(Set[1])
29
+ assert_equal [], @ssm.get(Set[2])
30
+ assert_equal [], @ssm.get(Set["fizz"])
31
+ assert_equal [], @ssm.get(Set["fazz"])
32
+ end
33
+
34
+ def test_disjoint_keys
35
+ assert_equal [], @ssm.get(Set[3, 4])
36
+ assert_equal [], @ssm.get(Set["fuzz", "frizz"])
37
+ assert_equal [], @ssm.get(Set["gran", 15])
38
+ end
39
+
40
+ def test_semi_disjoint_keys
41
+ assert_equal [], @ssm.get(Set[2, 3])
42
+ assert_equal [], @ssm.get(Set["fizz", "fuzz"])
43
+ assert_equal [], @ssm.get(Set[1, "fazz"])
44
+ end
45
+
46
+ def test_empty_key_set
47
+ assert_raises(ArgumentError) {@ssm[Set[]] = "Fail"}
48
+ end
49
+
50
+ def test_empty_key_get
51
+ assert_equal [], @ssm.get(Set[])
52
+ end
53
+
54
+ def test_multiple_subsets
55
+ assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, "fizz", "fazz"])
56
+ assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, 3, "fizz", "fazz", "fuzz"])
57
+
58
+ assert_equal [["Baz", Set[:foo, :bar]]], @ssm.get(Set[:foo, :bar])
59
+ assert_equal [["Baz", Set[:foo, :bar]], ["Bang", Set[:foo, :bar, :baz]]], @ssm.get(Set[:foo, :bar, :baz])
60
+ end
61
+
62
+ def test_bracket_bracket
63
+ assert_equal ["Foo"], @ssm[Set[1, 2, "fuzz"]]
64
+ assert_equal ["Baz", "Bang"], @ssm[Set[:foo, :bar, :baz]]
65
+ end
66
+
67
+ def test_order_preserved
68
+ @ssm[Set[10, 11, 12]] = 1
69
+ @ssm[Set[10, 11]] = 2
70
+ @ssm[Set[11]] = 3
71
+ @ssm[Set[11, 12]] = 4
72
+ @ssm[Set[9, 10, 11, 12, 13]] = 5
73
+ @ssm[Set[10, 13]] = 6
74
+
75
+ assert_equal(
76
+ [[1, Set[10, 11, 12]], [2, Set[10, 11]], [3, Set[11]], [4, Set[11, 12]],
77
+ [5, Set[9, 10, 11, 12, 13]], [6, Set[10, 13]]],
78
+ @ssm.get(Set[9, 10, 11, 12, 13]))
79
+ end
80
+
81
+ def test_multiple_equal_values
82
+ @ssm[Set[11, 12]] = 1
83
+ @ssm[Set[12, 13]] = 2
84
+ @ssm[Set[13, 14]] = 1
85
+ @ssm[Set[14, 15]] = 1
86
+
87
+ assert_equal(
88
+ [[1, Set[11, 12]], [2, Set[12, 13]], [1, Set[13, 14]], [1, Set[14, 15]]],
89
+ @ssm.get(Set[11, 12, 13, 14, 15]))
90
+ end
91
+ end
@@ -0,0 +1,438 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../test_helper'
3
+ require 'pathname'
4
+ require 'tmpdir'
5
+
6
+ class UtilTest < MiniTest::Test
7
+ include Sass::Util
8
+
9
+ def test_scope
10
+ assert(File.exist?(scope("Rakefile")))
11
+ end
12
+
13
+ def test_to_hash
14
+ assert_equal({
15
+ :foo => 1,
16
+ :bar => 2,
17
+ :baz => 3
18
+ }, to_hash([[:foo, 1], [:bar, 2], [:baz, 3]]))
19
+ end
20
+
21
+ def test_map_keys
22
+ assert_equal({
23
+ "foo" => 1,
24
+ "bar" => 2,
25
+ "baz" => 3
26
+ }, map_keys({:foo => 1, :bar => 2, :baz => 3}) {|k| k.to_s})
27
+ end
28
+
29
+ def test_map_vals
30
+ assert_equal({
31
+ :foo => "1",
32
+ :bar => "2",
33
+ :baz => "3"
34
+ }, map_vals({:foo => 1, :bar => 2, :baz => 3}) {|k| k.to_s})
35
+ end
36
+
37
+ def test_map_hash
38
+ assert_equal({
39
+ "foo" => "1",
40
+ "bar" => "2",
41
+ "baz" => "3"
42
+ }, map_hash({:foo => 1, :bar => 2, :baz => 3}) {|k, v| [k.to_s, v.to_s]})
43
+ end
44
+
45
+ def test_map_hash_with_normalized_map
46
+ map = NormalizedMap.new("foo-bar" => 1, "baz_bang" => 2)
47
+ result = map_hash(map) {|k, v| [k, v.to_s]}
48
+ assert_equal("1", result["foo-bar"])
49
+ assert_equal("1", result["foo_bar"])
50
+ assert_equal("2", result["baz-bang"])
51
+ assert_equal("2", result["baz_bang"])
52
+ end
53
+
54
+ def test_powerset
55
+ return unless Set[Set[]] == Set[Set[]] # There's a bug in Ruby 1.8.6 that breaks nested set equality
56
+ assert_equal([[].to_set].to_set,
57
+ powerset([]))
58
+ assert_equal([[].to_set, [1].to_set].to_set,
59
+ powerset([1]))
60
+ assert_equal([[].to_set, [1].to_set, [2].to_set, [1, 2].to_set].to_set,
61
+ powerset([1, 2]))
62
+ assert_equal([[].to_set, [1].to_set, [2].to_set, [3].to_set,
63
+ [1, 2].to_set, [2, 3].to_set, [1, 3].to_set, [1, 2, 3].to_set].to_set,
64
+ powerset([1, 2, 3]))
65
+ end
66
+
67
+ def test_restrict
68
+ assert_equal(0.5, restrict(0.5, 0..1))
69
+ assert_equal(1, restrict(2, 0..1))
70
+ assert_equal(1.3, restrict(2, 0..1.3))
71
+ assert_equal(0, restrict(-1, 0..1))
72
+ end
73
+
74
+ def test_merge_adjacent_strings
75
+ assert_equal(["foo bar baz", :bang, "biz bop", 12],
76
+ merge_adjacent_strings(["foo ", "bar ", "baz", :bang, "biz", " bop", 12]))
77
+ str = "foo"
78
+ assert_equal(["foo foo foo", :bang, "foo foo", 12],
79
+ merge_adjacent_strings([str, " ", str, " ", str, :bang, str, " ", str, 12]))
80
+ end
81
+
82
+ def test_replace_subseq
83
+ assert_equal([1, 2, :a, :b, 5],
84
+ replace_subseq([1, 2, 3, 4, 5], [3, 4], [:a, :b]))
85
+ assert_equal([1, 2, 3, 4, 5],
86
+ replace_subseq([1, 2, 3, 4, 5], [3, 4, 6], [:a, :b]))
87
+ assert_equal([1, 2, 3, 4, 5],
88
+ replace_subseq([1, 2, 3, 4, 5], [4, 5, 6], [:a, :b]))
89
+ end
90
+
91
+ def test_intersperse
92
+ assert_equal(["foo", " ", "bar", " ", "baz"],
93
+ intersperse(%w[foo bar baz], " "))
94
+ assert_equal([], intersperse([], " "))
95
+ end
96
+
97
+ def test_substitute
98
+ assert_equal(["foo", "bar", "baz", 3, 4],
99
+ substitute([1, 2, 3, 4], [1, 2], ["foo", "bar", "baz"]))
100
+ assert_equal([1, "foo", "bar", "baz", 4],
101
+ substitute([1, 2, 3, 4], [2, 3], ["foo", "bar", "baz"]))
102
+ assert_equal([1, 2, "foo", "bar", "baz"],
103
+ substitute([1, 2, 3, 4], [3, 4], ["foo", "bar", "baz"]))
104
+
105
+ assert_equal([1, "foo", "bar", "baz", 2, 3, 4],
106
+ substitute([1, 2, 2, 2, 3, 4], [2, 2], ["foo", "bar", "baz"]))
107
+ end
108
+
109
+ def test_strip_string_array
110
+ assert_equal(["foo ", " bar ", " baz"],
111
+ strip_string_array([" foo ", " bar ", " baz "]))
112
+ assert_equal([:foo, " bar ", " baz"],
113
+ strip_string_array([:foo, " bar ", " baz "]))
114
+ assert_equal(["foo ", " bar ", :baz],
115
+ strip_string_array([" foo ", " bar ", :baz]))
116
+ end
117
+
118
+ def test_paths
119
+ assert_equal([[1, 3, 5], [2, 3, 5], [1, 4, 5], [2, 4, 5]],
120
+ paths([[1, 2], [3, 4], [5]]))
121
+ assert_equal([[]], paths([]))
122
+ assert_equal([[1, 2, 3]], paths([[1], [2], [3]]))
123
+ end
124
+
125
+ def test_lcs
126
+ assert_equal([1, 2, 3], lcs([1, 2, 3], [1, 2, 3]))
127
+ assert_equal([], lcs([], [1, 2, 3]))
128
+ assert_equal([], lcs([1, 2, 3], []))
129
+ assert_equal([1, 2, 3], lcs([5, 1, 4, 2, 3, 17], [0, 0, 1, 2, 6, 3]))
130
+
131
+ assert_equal([1], lcs([1, 2, 3, 4], [4, 3, 2, 1]))
132
+ assert_equal([1, 2], lcs([1, 2, 3, 4], [3, 4, 1, 2]))
133
+ end
134
+
135
+ def test_lcs_with_block
136
+ assert_equal(["1", "2", "3"],
137
+ lcs([1, 4, 2, 5, 3], [1, 2, 3]) {|a, b| a == b && a.to_s})
138
+ assert_equal([-4, 2, 8],
139
+ lcs([-5, 3, 2, 8], [-4, 1, 8]) {|a, b| (a - b).abs <= 1 && [a, b].max})
140
+ end
141
+
142
+ def test_group_by_to_a
143
+ assert_equal([[1, [1, 3, 5, 7]], [0, [2, 4, 6, 8]]],
144
+ group_by_to_a(1..8) {|i| i % 2})
145
+ assert_equal([[1, [1, 4, 7, 10]], [2, [2, 5, 8, 11]], [0, [3, 6, 9, 12]]],
146
+ group_by_to_a(1..12) {|i| i % 3})
147
+ end
148
+
149
+ def test_subsequence
150
+ assert(subsequence?([1, 2, 3], [1, 2, 3]))
151
+ assert(subsequence?([1, 2, 3], [1, :a, 2, :b, 3]))
152
+ assert(subsequence?([1, 2, 3], [:a, 1, :b, :c, 2, :d, 3, :e, :f]))
153
+
154
+ assert(!subsequence?([1, 2, 3], [1, 2]))
155
+ assert(!subsequence?([1, 2, 3], [1, 3, 2]))
156
+ assert(!subsequence?([1, 2, 3], [3, 2, 1]))
157
+ end
158
+
159
+ def test_silence_warnings
160
+ old_stderr, $stderr = $stderr, StringIO.new
161
+ warn "Out"
162
+ assert_equal("Out\n", $stderr.string)
163
+ silence_warnings {warn "In"}
164
+ assert_equal("Out\n", $stderr.string)
165
+ ensure
166
+ $stderr = old_stderr
167
+ end
168
+
169
+ def test_sass_warn
170
+ assert_warning("Foo!") {sass_warn "Foo!"}
171
+ end
172
+
173
+ def test_silence_sass_warnings
174
+ old_stderr, $stderr = $stderr, StringIO.new
175
+ silence_sass_warnings {warn "Out"}
176
+ assert_equal("Out\n", $stderr.string)
177
+ silence_sass_warnings {sass_warn "In"}
178
+ assert_equal("Out\n", $stderr.string)
179
+ ensure
180
+ $stderr = old_stderr
181
+ end
182
+
183
+ def test_has
184
+ assert(has?(:instance_method, String, :chomp!))
185
+ assert(has?(:private_instance_method, Sass::Engine, :parse_interp))
186
+ end
187
+
188
+ def test_enum_with_index
189
+ assert_equal(%w[foo0 bar1 baz2],
190
+ enum_with_index(%w[foo bar baz]).map {|s, i| "#{s}#{i}"})
191
+ end
192
+
193
+ def test_enum_cons
194
+ assert_equal(%w[foobar barbaz],
195
+ enum_cons(%w[foo bar baz], 2).map {|s1, s2| "#{s1}#{s2}"})
196
+ end
197
+
198
+ def test_extract
199
+ arr = [1, 2, 3, 4, 5]
200
+ assert_equal([1, 3, 5], extract!(arr) {|e| e % 2 == 1})
201
+ assert_equal([2, 4], arr)
202
+ end
203
+
204
+ def test_ord
205
+ assert_equal(102, ord("f"))
206
+ assert_equal(98, ord("bar"))
207
+ end
208
+
209
+ def test_flatten_vertically
210
+ assert_equal([1, 2, 3], flatten_vertically([1, 2, 3]))
211
+ assert_equal([1, 3, 5, 2, 4, 6], flatten_vertically([[1, 2], [3, 4], [5, 6]]))
212
+ assert_equal([1, 2, 4, 3, 5, 6], flatten_vertically([1, [2, 3], [4, 5, 6]]))
213
+ assert_equal([1, 4, 6, 2, 5, 3], flatten_vertically([[1, 2, 3], [4, 5], 6]))
214
+ end
215
+
216
+ def test_extract_and_inject_values
217
+ test = lambda {|arr| assert_equal(arr, with_extracted_values(arr) {|str| str})}
218
+
219
+ test[['foo bar']]
220
+ test[['foo {12} bar']]
221
+ test[['foo {{12} bar']]
222
+ test[['foo {{1', 12, '2} bar']]
223
+ test[['foo 1', 2, '{3', 4, 5, 6, '{7}', 8]]
224
+ test[['foo 1', [2, 3, 4], ' bar']]
225
+ test[['foo ', 1, "\n bar\n", [2, 3, 4], "\n baz"]]
226
+ end
227
+
228
+ def nested_caller_info_fn
229
+ caller_info
230
+ end
231
+
232
+ def double_nested_caller_info_fn
233
+ nested_caller_info_fn
234
+ end
235
+
236
+ def test_caller_info
237
+ assert_equal(["/tmp/foo.rb", 12, "fizzle"], caller_info("/tmp/foo.rb:12: in `fizzle'"))
238
+ assert_equal(["/tmp/foo.rb", 12, nil], caller_info("/tmp/foo.rb:12"))
239
+ assert_equal(["C:/tmp/foo.rb", 12, nil], caller_info("C:/tmp/foo.rb:12"))
240
+ assert_equal(["(sass)", 12, "blah"], caller_info("(sass):12: in `blah'"))
241
+ assert_equal(["", 12, "boop"], caller_info(":12: in `boop'"))
242
+ assert_equal(["/tmp/foo.rb", -12, "fizzle"], caller_info("/tmp/foo.rb:-12: in `fizzle'"))
243
+ assert_equal(["/tmp/foo.rb", 12, "fizzle"], caller_info("/tmp/foo.rb:12: in `fizzle {}'"))
244
+ assert_equal(["C:/tmp/foo.rb", 12, "fizzle"], caller_info("C:/tmp/foo.rb:12: in `fizzle {}'"))
245
+
246
+ info = nested_caller_info_fn
247
+ assert_equal(__FILE__, info[0])
248
+ assert_equal("test_caller_info", info[2])
249
+
250
+ info = proc {nested_caller_info_fn}.call
251
+ assert_equal(__FILE__, info[0])
252
+ assert_match(/^(block in )?test_caller_info$/, info[2])
253
+
254
+ info = double_nested_caller_info_fn
255
+ assert_equal(__FILE__, info[0])
256
+ assert_equal("double_nested_caller_info_fn", info[2])
257
+
258
+ info = proc {double_nested_caller_info_fn}.call
259
+ assert_equal(__FILE__, info[0])
260
+ assert_equal("double_nested_caller_info_fn", info[2])
261
+ end
262
+
263
+ def test_version_gt
264
+ assert_version_gt("2.0.0", "1.0.0")
265
+ assert_version_gt("1.1.0", "1.0.0")
266
+ assert_version_gt("1.0.1", "1.0.0")
267
+ assert_version_gt("1.0.0", "1.0.0.rc")
268
+ assert_version_gt("1.0.0.1", "1.0.0.rc")
269
+ assert_version_gt("1.0.0.rc", "0.9.9")
270
+ assert_version_gt("1.0.0.beta", "1.0.0.alpha")
271
+
272
+ assert_version_eq("1.0.0", "1.0.0")
273
+ assert_version_eq("1.0.0", "1.0.0.0")
274
+ end
275
+
276
+ def assert_version_gt(v1, v2)
277
+ #assert(version_gt(v1, v2), "Expected #{v1} > #{v2}")
278
+ assert(!version_gt(v2, v1), "Expected #{v2} < #{v1}")
279
+ end
280
+
281
+ def assert_version_eq(v1, v2)
282
+ assert(!version_gt(v1, v2), "Expected #{v1} = #{v2}")
283
+ assert(!version_gt(v2, v1), "Expected #{v2} = #{v1}")
284
+ end
285
+
286
+ class FooBar
287
+ def foo
288
+ Sass::Util.abstract(self)
289
+ end
290
+ def old_method
291
+ Sass::Util.deprecated(self)
292
+ end
293
+ def old_method_with_custom_message
294
+ Sass::Util.deprecated(self, "Call FooBar#new_method instead.")
295
+ end
296
+ def self.another_old_method
297
+ Sass::Util.deprecated(self)
298
+ end
299
+ end
300
+
301
+ def test_abstract
302
+ assert_raise_message(NotImplementedError,
303
+ "UtilTest::FooBar must implement #foo") {FooBar.new.foo}
304
+ end
305
+
306
+ def test_deprecated
307
+ assert_warning("DEPRECATION WARNING: UtilTest::FooBar#old_method will be removed in a future version of Sass.") { FooBar.new.old_method }
308
+ assert_warning(<<WARNING) { FooBar.new.old_method_with_custom_message }
309
+ DEPRECATION WARNING: UtilTest::FooBar#old_method_with_custom_message will be removed in a future version of Sass.
310
+ Call FooBar#new_method instead.
311
+ WARNING
312
+ assert_warning("DEPRECATION WARNING: UtilTest::FooBar.another_old_method will be removed in a future version of Sass.") { FooBar.another_old_method }
313
+ end
314
+
315
+ def test_json_escape_string
316
+ assert_json_string "", ""
317
+ alphanum = (("0".."9").to_a).concat(("a".."z").to_a).concat(("A".."Z").to_a).join
318
+ assert_json_string alphanum, alphanum
319
+ assert_json_string "'\"\\'", "'\\\"\\\\'"
320
+ assert_json_string "\b\f\n\r\t", "\\b\\f\\n\\r\\t"
321
+ end
322
+
323
+ def assert_json_string(source, target)
324
+ assert_equal target, json_escape_string(source)
325
+ end
326
+
327
+ def test_json_value_of
328
+ assert_json_value 0, "0"
329
+ assert_json_value(-42, "-42")
330
+ assert_json_value 42, "42"
331
+ assert_json_value true, "true"
332
+ assert_json_value false, "false"
333
+ assert_json_value "", "\"\""
334
+ assert_json_value "\"\"", "\"\\\"\\\"\""
335
+ assert_json_value "Multi\nLine\rString", "\"Multi\\nLine\\rString\""
336
+ assert_json_value [1, "some\nstr,ing", false, nil], "[1,\"some\\nstr,ing\",false,null]"
337
+ end
338
+
339
+ def assert_json_value(source, target)
340
+ assert_equal target, json_value_of(source)
341
+ end
342
+
343
+ def test_vlq
344
+ assert_equal "A", encode_vlq(0)
345
+ assert_equal "e", encode_vlq(15)
346
+ assert_equal "gB", encode_vlq(16)
347
+ assert_equal "wH", encode_vlq(120)
348
+ end
349
+
350
+ def assert_vlq_encodes(int, vlq)
351
+ vlq_from_decimal_array = decimal_array.map {|d| encode_vlq(d)}.join
352
+ decimal_array_from_vlq = decode_vlq(vlq)
353
+ assert_equal vlq, vlq_from_decimal_array
354
+ assert_equal decimal_array, decimal_array_from_vlq
355
+ end
356
+
357
+ def test_round_respects_precision
358
+ original_precision = Sass::Script::Value::Number.precision
359
+ assert_equal 0, Sass::Util.round(0.49999)
360
+ assert_equal 1, Sass::Util.round(0.499999)
361
+ Sass::Script::Value::Number.precision = 6
362
+ assert_equal 0, Sass::Util.round(0.499999)
363
+ assert_equal 1, Sass::Util.round(0.49999999)
364
+ ensure
365
+ Sass::Script::Value::Number.precision = original_precision
366
+ end
367
+
368
+ def test_atomic_writes
369
+ # when using normal writes, this test fails about 90% of the time.
370
+ filename = File.join(Dir.tmpdir, "test_atomic")
371
+ 5.times do
372
+ writes_to_perform = %w(1 2 3 4 5 6 7 8 9).map {|i| "#{i}\n" * 100_000}
373
+ threads = writes_to_perform.map do |to_write|
374
+ Thread.new do
375
+ # To see this test fail with a normal write,
376
+ # change to the standard file open mechanism:
377
+ # open(filename, "w") do |f|
378
+ atomic_create_and_write_file(filename) do |f|
379
+ f.write(to_write)
380
+ end
381
+ end
382
+ end
383
+ loop do
384
+ contents = File.exist?(filename) ? File.read(filename) : nil
385
+ next if contents.nil? || contents.size == 0
386
+ unless writes_to_perform.include?(contents)
387
+ if contents.size != writes_to_perform.first.size
388
+ fail "Incomplete write detected: was #{contents.size} characters, " +
389
+ "should have been #{writes_to_perform.first.size}"
390
+ else
391
+ fail "Corrupted read/write detected"
392
+ end
393
+ end
394
+ break if threads.all? {|t| !t.alive?}
395
+ end
396
+ threads.each {|t| t.join}
397
+ end
398
+ ensure
399
+ Sass::Util.retry_on_windows {File.delete filename if File.exist?(filename)}
400
+ end
401
+
402
+ def test_atomic_write_permissions
403
+ atomic_filename = File.join(Dir.tmpdir, "test_atomic_perms.atomic")
404
+ normal_filename = File.join(Dir.tmpdir, "test_atomic_perms.normal")
405
+ atomic_create_and_write_file(atomic_filename) {|f| f.write("whatever\n") }
406
+ open(normal_filename, "wb") {|f| f.write("whatever\n") }
407
+ assert_equal File.stat(normal_filename).mode.to_s(8), File.stat(atomic_filename).mode.to_s(8)
408
+ ensure
409
+ File.unlink(atomic_filename) rescue nil
410
+ File.unlink(normal_filename) rescue nil
411
+ end
412
+
413
+ def test_atomic_writes_respect_umask
414
+ atomic_filename = File.join(Dir.tmpdir, "test_atomic_perms.atomic")
415
+ atomic_create_and_write_file(atomic_filename) do |f|
416
+ f.write("whatever\n")
417
+ end
418
+ assert_equal 0, File.stat(atomic_filename).mode & File.umask
419
+ ensure
420
+ File.unlink(atomic_filename)
421
+ end
422
+
423
+ class FakeError < RuntimeError; end
424
+
425
+ def test_atomic_writes_handles_exceptions
426
+ filename = File.join(Dir.tmpdir, "test_atomic_exception")
427
+ FileUtils.rm_f(filename)
428
+ tmp_filename = nil
429
+ assert_raises FakeError do
430
+ atomic_create_and_write_file(filename) do |f|
431
+ tmp_filename = f.path
432
+ raise FakeError.new "Borken"
433
+ end
434
+ end
435
+ assert !File.exist?(filename)
436
+ assert !File.exist?(tmp_filename)
437
+ end
438
+ end