sassc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +24 -0
  8. data/Rakefile +21 -0
  9. data/ext/libsass/.editorconfig +15 -0
  10. data/ext/libsass/.gitattributes +2 -0
  11. data/ext/libsass/.gitignore +61 -0
  12. data/ext/libsass/.travis.yml +38 -0
  13. data/ext/libsass/COPYING +25 -0
  14. data/ext/libsass/INSTALL +1 -0
  15. data/ext/libsass/LICENSE +25 -0
  16. data/ext/libsass/Makefile +223 -0
  17. data/ext/libsass/Makefile.am +145 -0
  18. data/ext/libsass/Readme.md +93 -0
  19. data/ext/libsass/appveyor.yml +76 -0
  20. data/ext/libsass/ast.cpp +581 -0
  21. data/ext/libsass/ast.hpp +1949 -0
  22. data/ext/libsass/ast_def_macros.hpp +16 -0
  23. data/ext/libsass/ast_factory.hpp +87 -0
  24. data/ext/libsass/ast_fwd_decl.hpp +72 -0
  25. data/ext/libsass/b64/cencode.h +32 -0
  26. data/ext/libsass/b64/encode.h +77 -0
  27. data/ext/libsass/backtrace.hpp +81 -0
  28. data/ext/libsass/base64vlq.cpp +43 -0
  29. data/ext/libsass/base64vlq.hpp +28 -0
  30. data/ext/libsass/bind.cpp +187 -0
  31. data/ext/libsass/bind.hpp +18 -0
  32. data/ext/libsass/cencode.c +102 -0
  33. data/ext/libsass/color_names.hpp +324 -0
  34. data/ext/libsass/configure.ac +130 -0
  35. data/ext/libsass/constants.cpp +144 -0
  36. data/ext/libsass/constants.hpp +145 -0
  37. data/ext/libsass/context.cpp +507 -0
  38. data/ext/libsass/context.hpp +150 -0
  39. data/ext/libsass/contextualize.cpp +157 -0
  40. data/ext/libsass/contextualize.hpp +65 -0
  41. data/ext/libsass/copy_c_str.cpp +13 -0
  42. data/ext/libsass/copy_c_str.hpp +5 -0
  43. data/ext/libsass/debug.hpp +39 -0
  44. data/ext/libsass/environment.hpp +75 -0
  45. data/ext/libsass/error_handling.cpp +28 -0
  46. data/ext/libsass/error_handling.hpp +28 -0
  47. data/ext/libsass/eval.cpp +1149 -0
  48. data/ext/libsass/eval.hpp +80 -0
  49. data/ext/libsass/expand.cpp +430 -0
  50. data/ext/libsass/expand.hpp +77 -0
  51. data/ext/libsass/extconf.rb +6 -0
  52. data/ext/libsass/extend.cpp +1962 -0
  53. data/ext/libsass/extend.hpp +50 -0
  54. data/ext/libsass/file.cpp +291 -0
  55. data/ext/libsass/file.hpp +18 -0
  56. data/ext/libsass/functions.cpp +1565 -0
  57. data/ext/libsass/functions.hpp +187 -0
  58. data/ext/libsass/inspect.cpp +727 -0
  59. data/ext/libsass/inspect.hpp +108 -0
  60. data/ext/libsass/json.cpp +1411 -0
  61. data/ext/libsass/json.hpp +117 -0
  62. data/ext/libsass/kwd_arg_macros.hpp +23 -0
  63. data/ext/libsass/m4/.gitkeep +0 -0
  64. data/ext/libsass/mapping.hpp +17 -0
  65. data/ext/libsass/memory_manager.hpp +54 -0
  66. data/ext/libsass/node.cpp +251 -0
  67. data/ext/libsass/node.hpp +122 -0
  68. data/ext/libsass/operation.hpp +153 -0
  69. data/ext/libsass/output_compressed.cpp +401 -0
  70. data/ext/libsass/output_compressed.hpp +95 -0
  71. data/ext/libsass/output_nested.cpp +364 -0
  72. data/ext/libsass/output_nested.hpp +108 -0
  73. data/ext/libsass/parser.cpp +2016 -0
  74. data/ext/libsass/parser.hpp +264 -0
  75. data/ext/libsass/paths.hpp +69 -0
  76. data/ext/libsass/position.hpp +22 -0
  77. data/ext/libsass/posix/getopt.c +562 -0
  78. data/ext/libsass/posix/getopt.h +95 -0
  79. data/ext/libsass/prelexer.cpp +688 -0
  80. data/ext/libsass/prelexer.hpp +513 -0
  81. data/ext/libsass/remove_placeholders.cpp +59 -0
  82. data/ext/libsass/remove_placeholders.hpp +43 -0
  83. data/ext/libsass/res/resource.rc +35 -0
  84. data/ext/libsass/sass.cpp +33 -0
  85. data/ext/libsass/sass.h +60 -0
  86. data/ext/libsass/sass2scss.cpp +834 -0
  87. data/ext/libsass/sass2scss.h +110 -0
  88. data/ext/libsass/sass_context.cpp +709 -0
  89. data/ext/libsass/sass_context.h +120 -0
  90. data/ext/libsass/sass_functions.cpp +137 -0
  91. data/ext/libsass/sass_functions.h +90 -0
  92. data/ext/libsass/sass_interface.cpp +277 -0
  93. data/ext/libsass/sass_interface.h +97 -0
  94. data/ext/libsass/sass_util.cpp +136 -0
  95. data/ext/libsass/sass_util.hpp +259 -0
  96. data/ext/libsass/sass_values.cpp +337 -0
  97. data/ext/libsass/sass_values.h +124 -0
  98. data/ext/libsass/script/bootstrap +10 -0
  99. data/ext/libsass/script/branding +10 -0
  100. data/ext/libsass/script/ci-build-libsass +72 -0
  101. data/ext/libsass/script/ci-install-compiler +4 -0
  102. data/ext/libsass/script/ci-install-deps +19 -0
  103. data/ext/libsass/script/ci-report-coverage +25 -0
  104. data/ext/libsass/script/coveralls-debug +32 -0
  105. data/ext/libsass/script/spec +5 -0
  106. data/ext/libsass/script/tap-driver +652 -0
  107. data/ext/libsass/script/tap-runner +1 -0
  108. data/ext/libsass/source_map.cpp +133 -0
  109. data/ext/libsass/source_map.hpp +46 -0
  110. data/ext/libsass/subset_map.hpp +145 -0
  111. data/ext/libsass/support/libsass.pc.in +11 -0
  112. data/ext/libsass/test-driver +127 -0
  113. data/ext/libsass/test/test_node.cpp +98 -0
  114. data/ext/libsass/test/test_paths.cpp +29 -0
  115. data/ext/libsass/test/test_selector_difference.cpp +28 -0
  116. data/ext/libsass/test/test_specificity.cpp +28 -0
  117. data/ext/libsass/test/test_subset_map.cpp +472 -0
  118. data/ext/libsass/test/test_superselector.cpp +71 -0
  119. data/ext/libsass/test/test_unification.cpp +33 -0
  120. data/ext/libsass/to_c.cpp +61 -0
  121. data/ext/libsass/to_c.hpp +44 -0
  122. data/ext/libsass/to_string.cpp +29 -0
  123. data/ext/libsass/to_string.hpp +32 -0
  124. data/ext/libsass/token.hpp +32 -0
  125. data/ext/libsass/units.cpp +54 -0
  126. data/ext/libsass/units.hpp +10 -0
  127. data/ext/libsass/utf8.h +34 -0
  128. data/ext/libsass/utf8/checked.h +327 -0
  129. data/ext/libsass/utf8/core.h +329 -0
  130. data/ext/libsass/utf8/unchecked.h +228 -0
  131. data/ext/libsass/utf8_string.cpp +102 -0
  132. data/ext/libsass/utf8_string.hpp +36 -0
  133. data/ext/libsass/util.cpp +189 -0
  134. data/ext/libsass/util.hpp +26 -0
  135. data/ext/libsass/win/libsass.filters +291 -0
  136. data/ext/libsass/win/libsass.sln +28 -0
  137. data/ext/libsass/win/libsass.vcxproj +255 -0
  138. data/lib/sassc.rb +6 -0
  139. data/lib/sassc/engine.rb +13 -0
  140. data/lib/sassc/native.rb +44 -0
  141. data/lib/sassc/native/native_context_api.rb +140 -0
  142. data/lib/sassc/native/native_functions_api.rb +41 -0
  143. data/lib/sassc/native/sass_input_style.rb +11 -0
  144. data/lib/sassc/native/sass_output_style.rb +10 -0
  145. data/lib/sassc/native/sass_value.rb +95 -0
  146. data/lib/sassc/native/string_list.rb +8 -0
  147. data/lib/sassc/version.rb +3 -0
  148. data/sassc.gemspec +43 -0
  149. data/test/smoke_test.rb +171 -0
  150. data/test/test_helper.rb +4 -0
  151. metadata +281 -0
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ script/branding
4
+
5
+ if [ ! -d "sass-spec" ]; then
6
+ git clone https://github.com/sass/sass-spec.git
7
+ fi
8
+ if [ ! -d "sassc" ]; then
9
+ git clone https://github.com/sass/sassc.git
10
+ fi
@@ -0,0 +1,10 @@
1
+ #! /bin/bash
2
+
3
+ echo " "
4
+ echo " _ ___ ____ ____ _ ____ ____ "
5
+ echo "| | |_ _| __ ) ___| / \ / ___/ ___| "
6
+ echo "| | | || _ \___ \ / _ \ \___ \___ \ "
7
+ echo "| |___ | || |_) |__) / ___ \ ___) |__) |"
8
+ echo "|_____|___|____/____/_/ \_\____/____/ "
9
+ echo " "
10
+
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ script/bootstrap
6
+
7
+ # export this path right here (was in script/spec before)
8
+ export SASS_LIBSASS_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )"
9
+
10
+ # use some defaults if not running under travis ci
11
+ if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then export TRAVIS_BUILD_DIR=$(pwd)/build; fi
12
+ if [ "x$SASS_SASSC_PATH" == "x" ]; then export SASS_SASSC_PATH=$(pwd)/sassc; fi
13
+ if [ "x$SASS_SPEC_PATH" == "x" ]; then export SASS_SPEC_PATH=$(pwd)/sass-spec; fi
14
+
15
+ if [ "x$COVERAGE" == "xyes" ]; then
16
+ COVERAGE_OPT="--enable-coverage"
17
+ export EXTRA_CFLAGS="-O0 --coverage"
18
+ export EXTRA_CXXFLAGS="-O0 --coverage"
19
+ export EXTRA_LDFLAGS="-O0 --coverage"
20
+ else
21
+ COVERAGE_OPT="--disable-coverage"
22
+ fi
23
+
24
+ if [ "x$BUILD" == "xstatic" ]; then
25
+ SHARED_OPT="--disable-shared --enable-static"
26
+ MAKE_TARGET="static"
27
+ else
28
+ # Makefile of sassc wants to link to static
29
+ SHARED_OPT="--enable-shared --enable-static"
30
+ MAKE_TARGET="shared"
31
+ fi
32
+
33
+ MAKE_OPTS="$MAKE_OPTS -j3 V=1"
34
+
35
+ if [ "x$AUTOTOOLS" == "xyes" ]; then
36
+
37
+ echo -en 'travis_fold:start:configure\r'
38
+ autoreconf --force --install
39
+ ./configure --enable-tests $COVERAGE_OPT \
40
+ --disable-silent-rules \
41
+ --with-sassc-dir=$SASS_SASSC_PATH \
42
+ --with-sass-spec-dir=$SASS_SPEC_PATH \
43
+ --prefix=$TRAVIS_BUILD_DIR \
44
+ ${SHARED_OPT}
45
+ echo -en 'travis_fold:end:configure\r'
46
+
47
+ make $MAKE_OPTS install
48
+
49
+ else
50
+
51
+ make clean
52
+
53
+ # does what $BUILD says
54
+ make $MAKE_OPTS
55
+
56
+ # sassc has static as dep
57
+ make $MAKE_OPTS $SASS_SASSC_PATH/bin/sassc
58
+
59
+ fi
60
+
61
+ echo successfully compiled libsass
62
+ echo AUTOTOOLS=$AUTOTOOLS COVERAGE=$COVERAGE BUILD=$BUILD
63
+
64
+ if [ "x$PREFIX" == "x" ]; then
65
+ if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then
66
+ PREFIX=/usr/local
67
+ else
68
+ PREFIX=$TRAVIS_BUILD_DIR
69
+ fi
70
+ fi
71
+
72
+ LD_LIBRARY_PATH="$PREFIX/lib/" script/spec
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ gem install minitest
4
+ gem install minitap
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ if [ "x$COVERAGE" == "xyes" ]; then
4
+ sudo pip install cpp-coveralls
5
+ # enable to debug coverage
6
+ sudo pip install gcovr
7
+ sudo apt-get install libjson-perl
8
+ sudo apt-get install libjson-xs-perl
9
+ sudo apt-get install libfile-slurp-perl
10
+ else
11
+ echo "no dependencies to install"
12
+ fi
13
+
14
+ if [ "x$AUTOTOOLS" == "xyes" ]; then
15
+ sudo add-apt-repository -y ppa:rbose-debianizer/automake &> /dev/null
16
+ sudo apt-get -qq update
17
+ sudo apt-get -qq install automake
18
+ AUTOTOOLS=yes
19
+ fi
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+
3
+ if [ "x$COVERAGE" = "xyes" ]; then
4
+
5
+ # exclude some directories from profiling (.libs is from autotools)
6
+ export EXCLUDE_COVERAGE="--exclude sassc --exclude sass-spec
7
+ --exclude .libs --exclude debug.hpp
8
+ --exclude json.cpp --exclude json.hpp
9
+ --exclude cencode.c --exclude b64
10
+ --exclude utf8 --exclude utf8_string.hpp
11
+ --exclude utf8.h --exclude utf8_string.cpp
12
+ --exclude test"
13
+ # debug via gcovr
14
+ gcov -v
15
+ gcovr -r .
16
+ # debug via coveralls (dump result for futher analyzing)
17
+ coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json
18
+ # analyze the resulting json
19
+ ./script/coveralls-debug
20
+ # generate and submit report to coveralls.io
21
+ coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp'
22
+
23
+ else
24
+ echo "skip coverage reporting"
25
+ fi
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/perl
2
+
3
+ use strict;
4
+ use warnings;
5
+ use JSON;
6
+ use File::Slurp;
7
+ my $json = JSON->new;
8
+ my $file = read_file('coveralls.json', { binmode => ':utf8' });
9
+ my $rv = $json->decode($file);
10
+ my $sources = $rv->{'source_files'};
11
+ print STDERR join ", ", keys %{$rv}, "\n";
12
+ foreach my $source (sort {
13
+ $a->{'name'} cmp $b->{'name'}
14
+ } @{$sources})
15
+ {
16
+ my $sum = 0;
17
+ my $undefs = 0;
18
+ my $coverages = $source->{'coverage'};
19
+ foreach my $coverage (@{$coverages})
20
+ {
21
+ if (defined $coverage)
22
+ { $sum += $coverage }
23
+ else { $undefs ++; }
24
+ }
25
+ if ($sum > 0)
26
+ {
27
+ print STDERR $source->{'name'};
28
+ print STDERR " [sum: $sum]";
29
+ print STDERR " [undefs: $undefs]";
30
+ print STDERR "\n";
31
+ }
32
+ }
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ script/bootstrap
4
+
5
+ make $MAKE_OPTS test_build
@@ -0,0 +1,652 @@
1
+ #! /bin/sh
2
+ # Copyright (C) 2011-2013 Free Software Foundation, Inc.
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2, or (at your option)
7
+ # any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # As a special exception to the GNU General Public License, if you
18
+ # distribute this file as part of a program that contains a
19
+ # configuration script generated by Autoconf, you may include it under
20
+ # the same distribution terms that you use for the rest of that program.
21
+
22
+ # This file is maintained in Automake, please report
23
+ # bugs to <bug-automake@gnu.org> or send patches to
24
+ # <automake-patches@gnu.org>.
25
+
26
+ scriptversion=2011-12-27.17; # UTC
27
+
28
+ # Make unconditional expansion of undefined variables an error. This
29
+ # helps a lot in preventing typo-related bugs.
30
+ set -u
31
+
32
+ me=tap-driver.sh
33
+
34
+ fatal ()
35
+ {
36
+ echo "$me: fatal: $*" >&2
37
+ exit 1
38
+ }
39
+
40
+ usage_error ()
41
+ {
42
+ echo "$me: $*" >&2
43
+ print_usage >&2
44
+ exit 2
45
+ }
46
+
47
+ print_usage ()
48
+ {
49
+ cat <<END
50
+ Usage:
51
+ tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
52
+ [--expect-failure={yes|no}] [--color-tests={yes|no}]
53
+ [--enable-hard-errors={yes|no}] [--ignore-exit]
54
+ [--diagnostic-string=STRING] [--merge|--no-merge]
55
+ [--comments|--no-comments] [--] TEST-COMMAND
56
+ The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory.
57
+ END
58
+ }
59
+
60
+ # TODO: better error handling in option parsing (in particular, ensure
61
+ # TODO: $log_file, $trs_file and $test_name are defined).
62
+ test_name= # Used for reporting.
63
+ log_file= # Where to save the result and output of the test script.
64
+ trs_file= # Where to save the metadata of the test run.
65
+ expect_failure=0
66
+ color_tests=0
67
+ merge=0
68
+ ignore_exit=0
69
+ comments=0
70
+ diag_string='#'
71
+ while test $# -gt 0; do
72
+ case $1 in
73
+ --help) print_usage; exit $?;;
74
+ --version) echo "$me $scriptversion"; exit $?;;
75
+ --test-name) test_name=$2; shift;;
76
+ --log-file) log_file=$2; shift;;
77
+ --trs-file) trs_file=$2; shift;;
78
+ --color-tests) color_tests=$2; shift;;
79
+ --expect-failure) expect_failure=$2; shift;;
80
+ --enable-hard-errors) shift;; # No-op.
81
+ --merge) merge=1;;
82
+ --no-merge) merge=0;;
83
+ --ignore-exit) ignore_exit=1;;
84
+ --comments) comments=1;;
85
+ --no-comments) comments=0;;
86
+ --diagnostic-string) diag_string=$2; shift;;
87
+ --) shift; break;;
88
+ -*) usage_error "invalid option: '$1'";;
89
+ esac
90
+ shift
91
+ done
92
+
93
+ test $# -gt 0 || usage_error "missing test command"
94
+
95
+ case $expect_failure in
96
+ yes) expect_failure=1;;
97
+ *) expect_failure=0;;
98
+ esac
99
+
100
+ if test $color_tests = yes; then
101
+ init_colors='
102
+ color_map["red"]="" # Red.
103
+ color_map["grn"]="" # Green.
104
+ color_map["lgn"]="" # Light green.
105
+ color_map["blu"]="" # Blue.
106
+ color_map["mgn"]="" # Magenta.
107
+ color_map["std"]="" # No color.
108
+ color_for_result["ERROR"] = "mgn"
109
+ color_for_result["PASS"] = "grn"
110
+ color_for_result["XPASS"] = "red"
111
+ color_for_result["FAIL"] = "red"
112
+ color_for_result["XFAIL"] = "lgn"
113
+ color_for_result["SKIP"] = "blu"'
114
+ else
115
+ init_colors=''
116
+ fi
117
+
118
+ # :; is there to work around a bug in bash 3.2 (and earlier) which
119
+ # does not always set '$?' properly on redirection failure.
120
+ # See the Autoconf manual for more details.
121
+ :;{
122
+ (
123
+ # Ignore common signals (in this subshell only!), to avoid potential
124
+ # problems with Korn shells. Some Korn shells are known to propagate
125
+ # to themselves signals that have killed a child process they were
126
+ # waiting for; this is done at least for SIGINT (and usually only for
127
+ # it, in truth). Without the `trap' below, such a behaviour could
128
+ # cause a premature exit in the current subshell, e.g., in case the
129
+ # test command it runs gets terminated by a SIGINT. Thus, the awk
130
+ # script we are piping into would never seen the exit status it
131
+ # expects on its last input line (which is displayed below by the
132
+ # last `echo $?' statement), and would thus die reporting an internal
133
+ # error.
134
+ # For more information, see the Autoconf manual and the threads:
135
+ # <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
136
+ # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
137
+ trap : 1 3 2 13 15
138
+ if test $merge -gt 0; then
139
+ exec 2>&1
140
+ else
141
+ exec 2>&3
142
+ fi
143
+ "$@"
144
+ echo $?
145
+ ) | LC_ALL=C ${AM_TAP_AWK-awk} \
146
+ -v me="$me" \
147
+ -v test_script_name="$test_name" \
148
+ -v log_file="$log_file" \
149
+ -v trs_file="$trs_file" \
150
+ -v expect_failure="$expect_failure" \
151
+ -v merge="$merge" \
152
+ -v ignore_exit="$ignore_exit" \
153
+ -v comments="$comments" \
154
+ -v diag_string="$diag_string" \
155
+ '
156
+ # FIXME: the usages of "cat >&3" below could be optimized when using
157
+ # FIXME: GNU awk, and/on on systems that supports /dev/fd/.
158
+
159
+ # Implementation note: in what follows, `result_obj` will be an
160
+ # associative array that (partly) simulates a TAP result object
161
+ # from the `TAP::Parser` perl module.
162
+
163
+ ## ----------- ##
164
+ ## FUNCTIONS ##
165
+ ## ----------- ##
166
+
167
+ function fatal(msg)
168
+ {
169
+ print me ": " msg | "cat >&2"
170
+ exit 1
171
+ }
172
+
173
+ function abort(where)
174
+ {
175
+ fatal("internal error " where)
176
+ }
177
+
178
+ # Convert a boolean to a "yes"/"no" string.
179
+ function yn(bool)
180
+ {
181
+ return bool ? "yes" : "no";
182
+ }
183
+
184
+ function add_test_result(result)
185
+ {
186
+ if (!test_results_index)
187
+ test_results_index = 0
188
+ test_results_list[test_results_index] = result
189
+ test_results_index += 1
190
+ test_results_seen[result] = 1;
191
+ }
192
+
193
+ # Whether the test script should be re-run by "make recheck".
194
+ function must_recheck()
195
+ {
196
+ for (k in test_results_seen)
197
+ if (k != "XFAIL" && k != "PASS" && k != "SKIP")
198
+ return 1
199
+ return 0
200
+ }
201
+
202
+ # Whether the content of the log file associated to this test should
203
+ # be copied into the "global" test-suite.log.
204
+ function copy_in_global_log()
205
+ {
206
+ for (k in test_results_seen)
207
+ if (k != "PASS")
208
+ return 1
209
+ return 0
210
+ }
211
+
212
+ # FIXME: this can certainly be improved ...
213
+ function get_global_test_result()
214
+ {
215
+ if ("ERROR" in test_results_seen)
216
+ return "ERROR"
217
+ if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
218
+ return "FAIL"
219
+ all_skipped = 1
220
+ for (k in test_results_seen)
221
+ if (k != "SKIP")
222
+ all_skipped = 0
223
+ if (all_skipped)
224
+ return "SKIP"
225
+ return "PASS";
226
+ }
227
+
228
+ function stringify_result_obj(result_obj)
229
+ {
230
+ if (result_obj["is_unplanned"] || result_obj["number"] != testno)
231
+ return "ERROR"
232
+
233
+ if (plan_seen == LATE_PLAN)
234
+ return "ERROR"
235
+
236
+ if (result_obj["directive"] == "TODO")
237
+ return result_obj["is_ok"] ? "XPASS" : "XFAIL"
238
+
239
+ if (result_obj["directive"] == "SKIP")
240
+ return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
241
+
242
+ if (length(result_obj["directive"]))
243
+ abort("in function stringify_result_obj()")
244
+
245
+ return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
246
+ }
247
+
248
+ function decorate_result(result)
249
+ {
250
+ color_name = color_for_result[result]
251
+ if (color_name)
252
+ return color_map[color_name] "" result "" color_map["std"]
253
+ # If we are not using colorized output, or if we do not know how
254
+ # to colorize the given result, we should return it unchanged.
255
+ return result
256
+ }
257
+
258
+ function report(result, details)
259
+ {
260
+ if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
261
+ {
262
+ msg = ": " test_script_name
263
+ add_test_result(result)
264
+ }
265
+ else if (result == "#")
266
+ {
267
+ msg = " " test_script_name ":"
268
+ }
269
+ else
270
+ {
271
+ abort("in function report()")
272
+ }
273
+ if (length(details))
274
+ msg = msg " " details
275
+ # Output on console might be colorized.
276
+ print decorate_result(result) msg
277
+ # Log the result in the log file too, to help debugging (this is
278
+ # especially true when said result is a TAP error or "Bail out!").
279
+ print result msg | "cat >&3";
280
+ }
281
+
282
+ function testsuite_error(error_message)
283
+ {
284
+ report("ERROR", "- " error_message)
285
+ }
286
+
287
+ function handle_tap_result()
288
+ {
289
+ details = result_obj["number"];
290
+ if (length(result_obj["description"]))
291
+ details = details " " result_obj["description"]
292
+
293
+ if (plan_seen == LATE_PLAN)
294
+ {
295
+ details = details " # AFTER LATE PLAN";
296
+ }
297
+ else if (result_obj["is_unplanned"])
298
+ {
299
+ details = details " # UNPLANNED";
300
+ }
301
+ else if (result_obj["number"] != testno)
302
+ {
303
+ details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
304
+ details, testno);
305
+ }
306
+ else if (result_obj["directive"])
307
+ {
308
+ details = details " # " result_obj["directive"];
309
+ if (length(result_obj["explanation"]))
310
+ details = details " " result_obj["explanation"]
311
+ }
312
+
313
+ report(stringify_result_obj(result_obj), details)
314
+ }
315
+
316
+ # `skip_reason` should be empty whenever planned > 0.
317
+ function handle_tap_plan(planned, skip_reason)
318
+ {
319
+ planned += 0 # Avoid getting confused if, say, `planned` is "00"
320
+ if (length(skip_reason) && planned > 0)
321
+ abort("in function handle_tap_plan()")
322
+ if (plan_seen)
323
+ {
324
+ # Error, only one plan per stream is acceptable.
325
+ testsuite_error("multiple test plans")
326
+ return;
327
+ }
328
+ planned_tests = planned
329
+ # The TAP plan can come before or after *all* the TAP results; we speak
330
+ # respectively of an "early" or a "late" plan. If we see the plan line
331
+ # after at least one TAP result has been seen, assume we have a late
332
+ # plan; in this case, any further test result seen after the plan will
333
+ # be flagged as an error.
334
+ plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
335
+ # If testno > 0, we have an error ("too many tests run") that will be
336
+ # automatically dealt with later, so do not worry about it here. If
337
+ # $plan_seen is true, we have an error due to a repeated plan, and that
338
+ # has already been dealt with above. Otherwise, we have a valid "plan
339
+ # with SKIP" specification, and should report it as a particular kind
340
+ # of SKIP result.
341
+ if (planned == 0 && testno == 0)
342
+ {
343
+ if (length(skip_reason))
344
+ skip_reason = "- " skip_reason;
345
+ report("SKIP", skip_reason);
346
+ }
347
+ }
348
+
349
+ function extract_tap_comment(line)
350
+ {
351
+ if (index(line, diag_string) == 1)
352
+ {
353
+ # Strip leading `diag_string` from `line`.
354
+ line = substr(line, length(diag_string) + 1)
355
+ # And strip any leading and trailing whitespace left.
356
+ sub("^[ \t]*", "", line)
357
+ sub("[ \t]*$", "", line)
358
+ # Return what is left (if any).
359
+ return line;
360
+ }
361
+ return "";
362
+ }
363
+
364
+ # When this function is called, we know that line is a TAP result line,
365
+ # so that it matches the (perl) RE "^(not )?ok\b".
366
+ function setup_result_obj(line)
367
+ {
368
+ # Get the result, and remove it from the line.
369
+ result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
370
+ sub("^(not )?ok[ \t]*", "", line)
371
+
372
+ # If the result has an explicit number, get it and strip it; otherwise,
373
+ # automatically assing the next progresive number to it.
374
+ if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
375
+ {
376
+ match(line, "^[0-9]+")
377
+ # The final `+ 0` is to normalize numbers with leading zeros.
378
+ result_obj["number"] = substr(line, 1, RLENGTH) + 0
379
+ line = substr(line, RLENGTH + 1)
380
+ }
381
+ else
382
+ {
383
+ result_obj["number"] = testno
384
+ }
385
+
386
+ if (plan_seen == LATE_PLAN)
387
+ # No further test results are acceptable after a "late" TAP plan
388
+ # has been seen.
389
+ result_obj["is_unplanned"] = 1
390
+ else if (plan_seen && testno > planned_tests)
391
+ result_obj["is_unplanned"] = 1
392
+ else
393
+ result_obj["is_unplanned"] = 0
394
+
395
+ # Strip trailing and leading whitespace.
396
+ sub("^[ \t]*", "", line)
397
+ sub("[ \t]*$", "", line)
398
+
399
+ # This will have to be corrected if we have a "TODO"/"SKIP" directive.
400
+ result_obj["description"] = line
401
+ result_obj["directive"] = ""
402
+ result_obj["explanation"] = ""
403
+
404
+ if (index(line, "#") == 0)
405
+ return # No possible directive, nothing more to do.
406
+
407
+ # Directives are case-insensitive.
408
+ rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
409
+
410
+ # See whether we have the directive, and if yes, where.
411
+ pos = match(line, rx "$")
412
+ if (!pos)
413
+ pos = match(line, rx "[^a-zA-Z0-9_]")
414
+
415
+ # If there was no TAP directive, we have nothing more to do.
416
+ if (!pos)
417
+ return
418
+
419
+ # Let`s now see if the TAP directive has been escaped. For example:
420
+ # escaped: ok \# SKIP
421
+ # not escaped: ok \\# SKIP
422
+ # escaped: ok \\\\\# SKIP
423
+ # not escaped: ok \ # SKIP
424
+ if (substr(line, pos, 1) == "#")
425
+ {
426
+ bslash_count = 0
427
+ for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
428
+ bslash_count += 1
429
+ if (bslash_count % 2)
430
+ return # Directive was escaped.
431
+ }
432
+
433
+ # Strip the directive and its explanation (if any) from the test
434
+ # description.
435
+ result_obj["description"] = substr(line, 1, pos - 1)
436
+ # Now remove the test description from the line, that has been dealt
437
+ # with already.
438
+ line = substr(line, pos)
439
+ # Strip the directive, and save its value (normalized to upper case).
440
+ sub("^[ \t]*#[ \t]*", "", line)
441
+ result_obj["directive"] = toupper(substr(line, 1, 4))
442
+ line = substr(line, 5)
443
+ # Now get the explanation for the directive (if any), with leading
444
+ # and trailing whitespace removed.
445
+ sub("^[ \t]*", "", line)
446
+ sub("[ \t]*$", "", line)
447
+ result_obj["explanation"] = line
448
+ }
449
+
450
+ function get_test_exit_message(status)
451
+ {
452
+ if (status == 0)
453
+ return ""
454
+ if (status !~ /^[1-9][0-9]*$/)
455
+ abort("getting exit status")
456
+ if (status < 127)
457
+ exit_details = ""
458
+ else if (status == 127)
459
+ exit_details = " (command not found?)"
460
+ else if (status >= 128 && status <= 255)
461
+ exit_details = sprintf(" (terminated by signal %d?)", status - 128)
462
+ else if (status > 256 && status <= 384)
463
+ # We used to report an "abnormal termination" here, but some Korn
464
+ # shells, when a child process die due to signal number n, can leave
465
+ # in $? an exit status of 256+n instead of the more standard 128+n.
466
+ # Apparently, both behaviours are allowed by POSIX (2008), so be
467
+ # prepared to handle them both. See also Austing Group report ID
468
+ # 0000051 <http://www.austingroupbugs.net/view.php?id=51>
469
+ exit_details = sprintf(" (terminated by signal %d?)", status - 256)
470
+ else
471
+ # Never seen in practice.
472
+ exit_details = " (abnormal termination)"
473
+ return sprintf("exited with status %d%s", status, exit_details)
474
+ }
475
+
476
+ function write_test_results()
477
+ {
478
+ print ":global-test-result: " get_global_test_result() > trs_file
479
+ print ":recheck: " yn(must_recheck()) > trs_file
480
+ print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
481
+ for (i = 0; i < test_results_index; i += 1)
482
+ print ":test-result: " test_results_list[i] > trs_file
483
+ close(trs_file);
484
+ }
485
+
486
+ BEGIN {
487
+
488
+ ## ------- ##
489
+ ## SETUP ##
490
+ ## ------- ##
491
+
492
+ '"$init_colors"'
493
+
494
+ # Properly initialized once the TAP plan is seen.
495
+ planned_tests = 0
496
+
497
+ COOKED_PASS = expect_failure ? "XPASS": "PASS";
498
+ COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
499
+
500
+ # Enumeration-like constants to remember which kind of plan (if any)
501
+ # has been seen. It is important that NO_PLAN evaluates "false" as
502
+ # a boolean.
503
+ NO_PLAN = 0
504
+ EARLY_PLAN = 1
505
+ LATE_PLAN = 2
506
+
507
+ testno = 0 # Number of test results seen so far.
508
+ bailed_out = 0 # Whether a "Bail out!" directive has been seen.
509
+
510
+ # Whether the TAP plan has been seen or not, and if yes, which kind
511
+ # it is ("early" is seen before any test result, "late" otherwise).
512
+ plan_seen = NO_PLAN
513
+
514
+ ## --------- ##
515
+ ## PARSING ##
516
+ ## --------- ##
517
+
518
+ is_first_read = 1
519
+
520
+ while (1)
521
+ {
522
+ # Involutions required so that we are able to read the exit status
523
+ # from the last input line.
524
+ st = getline
525
+ if (st < 0) # I/O error.
526
+ fatal("I/O error while reading from input stream")
527
+ else if (st == 0) # End-of-input
528
+ {
529
+ if (is_first_read)
530
+ abort("in input loop: only one input line")
531
+ break
532
+ }
533
+ if (is_first_read)
534
+ {
535
+ is_first_read = 0
536
+ nextline = $0
537
+ continue
538
+ }
539
+ else
540
+ {
541
+ curline = nextline
542
+ nextline = $0
543
+ $0 = curline
544
+ }
545
+ # Copy any input line verbatim into the log file.
546
+ print | "cat >&3"
547
+ # Parsing of TAP input should stop after a "Bail out!" directive.
548
+ if (bailed_out)
549
+ continue
550
+
551
+ # TAP test result.
552
+ if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
553
+ {
554
+ testno += 1
555
+ setup_result_obj($0)
556
+ handle_tap_result()
557
+ }
558
+ # TAP plan (normal or "SKIP" without explanation).
559
+ else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
560
+ {
561
+ # The next two lines will put the number of planned tests in $0.
562
+ sub("^1\\.\\.", "")
563
+ sub("[^0-9]*$", "")
564
+ handle_tap_plan($0, "")
565
+ continue
566
+ }
567
+ # TAP "SKIP" plan, with an explanation.
568
+ else if ($0 ~ /^1\.\.0+[ \t]*#/)
569
+ {
570
+ # The next lines will put the skip explanation in $0, stripping
571
+ # any leading and trailing whitespace. This is a little more
572
+ # tricky in truth, since we want to also strip a potential leading
573
+ # "SKIP" string from the message.
574
+ sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
575
+ sub("[ \t]*$", "");
576
+ handle_tap_plan(0, $0)
577
+ }
578
+ # "Bail out!" magic.
579
+ # Older versions of prove and TAP::Harness (e.g., 3.17) did not
580
+ # recognize a "Bail out!" directive when preceded by leading
581
+ # whitespace, but more modern versions (e.g., 3.23) do. So we
582
+ # emulate the latter, "more modern" behaviour.
583
+ else if ($0 ~ /^[ \t]*Bail out!/)
584
+ {
585
+ bailed_out = 1
586
+ # Get the bailout message (if any), with leading and trailing
587
+ # whitespace stripped. The message remains stored in `$0`.
588
+ sub("^[ \t]*Bail out![ \t]*", "");
589
+ sub("[ \t]*$", "");
590
+ # Format the error message for the
591
+ bailout_message = "Bail out!"
592
+ if (length($0))
593
+ bailout_message = bailout_message " " $0
594
+ testsuite_error(bailout_message)
595
+ }
596
+ # Maybe we have too look for dianogtic comments too.
597
+ else if (comments != 0)
598
+ {
599
+ comment = extract_tap_comment($0);
600
+ if (length(comment))
601
+ report("#", comment);
602
+ }
603
+ }
604
+
605
+ ## -------- ##
606
+ ## FINISH ##
607
+ ## -------- ##
608
+
609
+ # A "Bail out!" directive should cause us to ignore any following TAP
610
+ # error, as well as a non-zero exit status from the TAP producer.
611
+ if (!bailed_out)
612
+ {
613
+ if (!plan_seen)
614
+ {
615
+ testsuite_error("missing test plan")
616
+ }
617
+ else if (planned_tests != testno)
618
+ {
619
+ bad_amount = testno > planned_tests ? "many" : "few"
620
+ testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
621
+ bad_amount, planned_tests, testno))
622
+ }
623
+ if (!ignore_exit)
624
+ {
625
+ # Fetch exit status from the last line.
626
+ exit_message = get_test_exit_message(nextline)
627
+ if (exit_message)
628
+ testsuite_error(exit_message)
629
+ }
630
+ }
631
+
632
+ write_test_results()
633
+
634
+ exit 0
635
+
636
+ } # End of "BEGIN" block.
637
+ '
638
+
639
+ # TODO: document that we consume the file descriptor 3 :-(
640
+ } 3>"$log_file"
641
+
642
+ test $? -eq 0 || fatal "I/O or internal error"
643
+
644
+ # Local Variables:
645
+ # mode: shell-script
646
+ # sh-indentation: 2
647
+ # eval: (add-hook 'write-file-hooks 'time-stamp)
648
+ # time-stamp-start: "scriptversion="
649
+ # time-stamp-format: "%:y-%02m-%02d.%02H"
650
+ # time-stamp-time-zone: "UTC"
651
+ # time-stamp-end: "; # UTC"
652
+ # End: