sassc 0.0.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 (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: