commit 87dd2ff6d3d7e9b644c7366a933151c3d240c5d8
parent 0115452a17b566c5810acfeb4bb16e0a353048f9
Author: Jan Pobrislo <ccx@te2000.cz>
Date: Fri, 25 Apr 2025 20:06:18 +0000
Add tests for spec.pl
Diffstat:
5 files changed, 142 insertions(+), 56 deletions(-)
diff --git a/doc/Makefile b/doc/Makefile
@@ -1,3 +1,10 @@
+all: spec.html test
+.PHONY: all
+
spec.html: spec.pl spec2html.awk
awk -f spec2html.awk spec.pl >'$@.new'
mv '$@.new' '$@'
+
+test:
+ swipl -g run_tests -t halt tests.pl
+.PHONY: test
diff --git a/doc/spec.html b/doc/spec.html
@@ -9,7 +9,7 @@
<tr><td>7</td><td><pre></pre></td><td rowspan="4"></td></tr>
<tr><td>8</td><td><pre>% apply check to value when bound</pre></td></tr>
<tr><td>9</td><td><pre>will_be(Value, Goal) :-</pre></td></tr>
-<tr><td>10</td><td><pre> freeze(Value, call(Goal, Value)).</pre></td></tr>
+<tr><td>10</td><td><pre> freeze(Value, assertion(call(Goal, Value))).</pre></td></tr>
<tr><td>11</td><td><pre></pre></td><td rowspan="2"></td></tr>
<tr><td>12</td><td><pre>byte(Value) :- must_be(between(0, 255), Value).</pre></td></tr>
<tr><td>13</td><td><pre></pre></td><td rowspan="2"></td></tr>
@@ -20,41 +20,56 @@
<tr><td>18</td><td><pre> First will_be byte,</pre></td></tr>
<tr><td>19</td><td><pre> Rest will_be sequence_of_bytes.</pre></td></tr>
<tr><td>20</td><td><pre></pre></td><td rowspan="1"></td></tr>
-<tr><td>21</td><td><pre>netstring_encoding(PayloadLength, PayloadBytes) -->gt; %<lt; Netstring is a sequence of bytes.</pre></td><td rowspan="2"></td></tr>
-<tr><td>22</td><td><pre> { length(PayloadBytes, PayloadLength) },</pre></td></tr>
-<tr><td>23</td><td><pre> netstring_prefix(PayloadLength), %<lt; It consists of prefix,</pre></td><td rowspan="1"></td></tr>
-<tr><td>24</td><td><pre> PayloadBytes, %<lt; payload,</pre></td><td rowspan="1"></td></tr>
-<tr><td>25</td><td><pre> `,`. %<lt; and terminator. Terminator is single ASCII comma `,`.</pre></td><td rowspan="5"></td></tr>
-<tr><td>26</td><td><pre>netstring_encoding(netstring(PayloadLength, PayloadBytes), Bytes) :-</pre></td></tr>
+<tr><td>21</td><td><pre>netstring_encoding(PayloadBytes) -->gt; </pre></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr>
+<tr><td>22</td><td><pre> netstring_prefix_for_payload(PayloadBytes), </pre></td><td rowspan="1">It consists of prefix,</td></tr>
+<tr><td>23</td><td><pre> PayloadBytes, </pre></td><td rowspan="1">payload,</td></tr>
+<tr><td>24</td><td><pre> `,`. </pre></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr>
+<tr><td>25</td><td><pre>netstring_encoding(netstring(PayloadBytes), Bytes) :-</pre></td></tr>
+<tr><td>26</td><td><pre> assertion(ground(PayloadBytes); ground(Bytes)),</pre></td></tr>
<tr><td>27</td><td><pre> Bytes will_be sequence_of_bytes,</pre></td></tr>
-<tr><td>28</td><td><pre> PayloadLength will_be nonnegative_integer,</pre></td></tr>
-<tr><td>29</td><td><pre> phrase(netstring_encoding(PayloadLength, PayloadBytes), Bytes).</pre></td></tr>
-<tr><td>30</td><td><pre></pre></td><td rowspan="4">
-Netstring prefix is the shortest ASCII decimal representation for length of payload in bytes, followed by ASCII colon `:`. That is number starting with non-zero digit unless payload is empty, in which case it's `0`.</td></tr>
+<tr><td>28</td><td><pre> phrase(netstring_encoding(PayloadBytes), Bytes).</pre></td></tr>
+<tr><td>29</td><td><pre></pre></td><td rowspan="1"></td></tr>
+<tr><td>30</td><td><pre></pre></td><td rowspan="19">Netstring prefix is the shortest ASCII decimal representation for length of
+payload in bytes, followed by ASCII colon `:`.
+That is number starting with non-zero digit unless payload is empty,
+in which case it's `0`.</td></tr>
<tr><td>31</td><td><pre></pre></td></tr>
-<tr><td>32</td><td><pre>netstring_prefix(0) -->gt; `0:`.</pre></td></tr>
-<tr><td>33</td><td><pre>netstring_prefix(N) -->gt; positive_decimal(N), `:`.</pre></td></tr>
-<tr><td>34</td><td><pre></pre></td><td rowspan="7"></td></tr>
-<tr><td>35</td><td><pre>positive_decimal(N) -->gt;</pre></td></tr>
-<tr><td>36</td><td><pre> { freeze(N, format(codes([C|Cs]), '~w', N)) },</pre></td></tr>
-<tr><td>37</td><td><pre> nonzero_digit(C),</pre></td></tr>
-<tr><td>38</td><td><pre> digits(Cs),</pre></td></tr>
-<tr><td>39</td><td><pre> { phrase(number(N), [C|Cs]) }.</pre></td></tr>
-<tr><td>40</td><td><pre>nonzero_digit(Code) -->gt; [Code], {member(Code, `123456789`)}.</pre></td></tr>
-<tr><td>41</td><td><pre></pre></td><td rowspan="6"></td></tr>
-<tr><td>42</td><td><pre>netstring_of(Goal, Bytes) :-</pre></td></tr>
-<tr><td>43</td><td><pre> ground(Goal),</pre></td></tr>
-<tr><td>44</td><td><pre> !,</pre></td></tr>
-<tr><td>45</td><td><pre> call(Goal, PayloadBytes),</pre></td></tr>
-<tr><td>46</td><td><pre> netstring_encoding(netstring(_, PayloadBytes), Bytes).</pre></td></tr>
-<tr><td>47</td><td><pre></pre></td><td rowspan="6"></td></tr>
-<tr><td>48</td><td><pre>netstring_of(Goal, Bytes) :-</pre></td></tr>
-<tr><td>49</td><td><pre> ground(Bytes),</pre></td></tr>
-<tr><td>50</td><td><pre> !,</pre></td></tr>
-<tr><td>51</td><td><pre> netstring_encoding(netstring(_, PayloadBytes), Bytes),</pre></td></tr>
-<tr><td>52</td><td><pre> call(Goal, PayloadBytes).</pre></td></tr>
-<tr><td>53</td><td><pre></pre></td><td rowspan="1"></td></tr>
-<tr><td>54</td><td><pre></pre></td><td rowspan="3"></td></tr>
-<tr><td>55</td><td><pre>% :- use_module(library(pldoc/doc_files)).</pre></td></tr>
-<tr><td>56</td><td><pre>% doc_save('spec.pl', [format(html), doc_root(.)]).</pre></td></tr>
+<tr><td>32</td><td><pre></pre></td></tr>
+<tr><td>33</td><td><pre></pre></td></tr>
+<tr><td>34</td><td><pre>netstring_prefix_for_payload(PayloadBytes, A, B) :-</pre></td></tr>
+<tr><td>35</td><td><pre> ( var(PayloadBytes)</pre></td></tr>
+<tr><td>36</td><td><pre> ->gt; netstring_prefix_codes(Prefix, A, B),</pre></td></tr>
+<tr><td>37</td><td><pre> number_codes(PayloadLength, Prefix),</pre></td></tr>
+<tr><td>38</td><td><pre> length(PayloadBytes, PayloadLength)</pre></td></tr>
+<tr><td>39</td><td><pre> ; length(PayloadBytes, PayloadLength),</pre></td></tr>
+<tr><td>40</td><td><pre> number_codes(PayloadLength, Prefix),</pre></td></tr>
+<tr><td>41</td><td><pre> netstring_prefix_codes(Prefix, A, B)</pre></td></tr>
+<tr><td>42</td><td><pre> ).</pre></td></tr>
+<tr><td>43</td><td><pre>netstring_prefix_codes(`0`) -->gt; `0:`, !.</pre></td></tr>
+<tr><td>44</td><td><pre>netstring_prefix_codes([C|Cs]) -->gt;</pre></td></tr>
+<tr><td>45</td><td><pre> nonzero_digit(C),</pre></td></tr>
+<tr><td>46</td><td><pre> !,</pre></td></tr>
+<tr><td>47</td><td><pre> digits(Cs),</pre></td></tr>
+<tr><td>48</td><td><pre> `:`.</pre></td></tr>
+<tr><td>49</td><td><pre></pre></td><td rowspan="5"></td></tr>
+<tr><td>50</td><td><pre>nonzero_digit(Code) -->gt;</pre></td></tr>
+<tr><td>51</td><td><pre> [Code],</pre></td></tr>
+<tr><td>52</td><td><pre> { assertion(ground(Code)) },</pre></td></tr>
+<tr><td>53</td><td><pre> { member(Code, `123456789`) }.</pre></td></tr>
+<tr><td>54</td><td><pre></pre></td><td rowspan="6"></td></tr>
+<tr><td>55</td><td><pre>netstring_of(Goal, Bytes) :-</pre></td></tr>
+<tr><td>56</td><td><pre> ground(Goal),</pre></td></tr>
+<tr><td>57</td><td><pre> !,</pre></td></tr>
+<tr><td>58</td><td><pre> call(Goal, PayloadBytes),</pre></td></tr>
+<tr><td>59</td><td><pre> netstring_encoding(netstring(PayloadBytes), Bytes).</pre></td></tr>
+<tr><td>60</td><td><pre></pre></td><td rowspan="6"></td></tr>
+<tr><td>61</td><td><pre>netstring_of(Goal, Bytes) :-</pre></td></tr>
+<tr><td>62</td><td><pre> ground(Bytes),</pre></td></tr>
+<tr><td>63</td><td><pre> !,</pre></td></tr>
+<tr><td>64</td><td><pre> netstring_encoding(netstring(PayloadBytes), Bytes),</pre></td></tr>
+<tr><td>65</td><td><pre> call(Goal, PayloadBytes).</pre></td></tr>
+<tr><td>66</td><td><pre></pre></td><td rowspan="1"></td></tr>
+<tr><td>67</td><td><pre></pre></td><td rowspan="3"></td></tr>
+<tr><td>68</td><td><pre>% :- use_module(library(pldoc/doc_files)).</pre></td></tr>
+<tr><td>69</td><td><pre>% doc_save('spec.pl', [format(html), doc_root(.)]).</pre></td></tr>
</table></body></html>
diff --git a/doc/spec.pl b/doc/spec.pl
@@ -7,7 +7,7 @@
% apply check to value when bound
will_be(Value, Goal) :-
- freeze(Value, call(Goal, Value)).
+ freeze(Value, assertion(call(Goal, Value))).
byte(Value) :- must_be(between(0, 255), Value).
@@ -18,37 +18,50 @@ sequence_of_bytes([First|Rest]) :-
First will_be byte,
Rest will_be sequence_of_bytes.
-netstring_encoding(PayloadLength, PayloadBytes) --> %< Netstring is a sequence of bytes.
- { length(PayloadBytes, PayloadLength) },
- netstring_prefix(PayloadLength), %< It consists of prefix,
- PayloadBytes, %< payload,
- `,`. %< and terminator. Terminator is single ASCII comma `,`.
-netstring_encoding(netstring(PayloadLength, PayloadBytes), Bytes) :-
+netstring_encoding(PayloadBytes) --> %> Netstring is a sequence of bytes.
+ netstring_prefix_for_payload(PayloadBytes), %> It consists of prefix,
+ PayloadBytes, %> payload,
+ `,`. %> and terminator. Terminator is single ASCII comma `,`.
+netstring_encoding(netstring(PayloadBytes), Bytes) :-
+ assertion(ground(PayloadBytes); ground(Bytes)),
Bytes will_be sequence_of_bytes,
- PayloadLength will_be nonnegative_integer,
- phrase(netstring_encoding(PayloadLength, PayloadBytes), Bytes).
+ phrase(netstring_encoding(PayloadBytes), Bytes).
-%> Netstring prefix is the shortest ASCII decimal representation for length of payload in bytes, followed by ASCII colon `:`. That is number starting with non-zero digit unless payload is empty, in which case it's `0`.
-netstring_prefix(0) --> `0:`.
-netstring_prefix(N) --> positive_decimal(N), `:`.
-
-positive_decimal(N) -->
- { freeze(N, format(codes([C|Cs]), '~w', N)) },
+%> Netstring prefix is the shortest ASCII decimal representation for length of
+%^ payload in bytes, followed by ASCII colon `:`.
+%^ That is number starting with non-zero digit unless payload is empty,
+%^ in which case it's `0`.
+netstring_prefix_for_payload(PayloadBytes, A, B) :-
+ ( var(PayloadBytes)
+ -> netstring_prefix_codes(Prefix, A, B),
+ number_codes(PayloadLength, Prefix),
+ length(PayloadBytes, PayloadLength)
+ ; length(PayloadBytes, PayloadLength),
+ number_codes(PayloadLength, Prefix),
+ netstring_prefix_codes(Prefix, A, B)
+ ).
+netstring_prefix_codes(`0`) --> `0:`, !.
+netstring_prefix_codes([C|Cs]) -->
nonzero_digit(C),
+ !,
digits(Cs),
- { phrase(number(N), [C|Cs]) }.
-nonzero_digit(Code) --> [Code], {member(Code, `123456789`)}.
+ `:`.
+
+nonzero_digit(Code) -->
+ [Code],
+ { assertion(ground(Code)) },
+ { member(Code, `123456789`) }.
netstring_of(Goal, Bytes) :-
ground(Goal),
!,
call(Goal, PayloadBytes),
- netstring_encoding(netstring(_, PayloadBytes), Bytes).
+ netstring_encoding(netstring(PayloadBytes), Bytes).
netstring_of(Goal, Bytes) :-
ground(Bytes),
!,
- netstring_encoding(netstring(_, PayloadBytes), Bytes),
+ netstring_encoding(netstring(PayloadBytes), Bytes),
call(Goal, PayloadBytes).
diff --git a/doc/spec2html.awk b/doc/spec2html.awk
@@ -7,7 +7,7 @@ BEGIN {
last_comm = 1
}
-/%< / || /^$/{
+/%> / || /^$/{
last_comm = NR
}
diff --git a/doc/tests.pl b/doc/tests.pl
@@ -0,0 +1,51 @@
+:- begin_tests(spec_netstring).
+:- [spec].
+
+test(prefix_encode_empty) :-
+ PayloadBytes = ``,
+ phrase(netstring_prefix_for_payload(PayloadBytes), PrefixBytes),
+ assertion(PrefixBytes =@= `0:`).
+
+test(prefix_decode_empty) :-
+ PrefixBytes = `0:`,
+ phrase(netstring_prefix_for_payload(PayloadBytes), PrefixBytes),
+ assertion(PayloadBytes =@= ``).
+
+test(encode_empty) :-
+ netstring_encoding(netstring(``), Bytes),
+ assertion(Bytes =@= `0:,`).
+
+test(decode_empty) :-
+ netstring_encoding(netstring(PayloadBytes), `0:,`),
+ assertion(PayloadBytes =@= ``).
+
+test(encode_single_null) :-
+ netstring_encoding(netstring(`\0`), Bytes),
+ assertion(Bytes =@= `1:\0,`).
+
+test(decode_single_null) :-
+ netstring_encoding(netstring(PayloadBytes), `1:\0,`),
+ assertion(PayloadBytes =@= `\0`).
+
+test(encode_ten_bytes) :-
+ netstring_encoding(netstring(`1234567890`), Bytes),
+ assertion(Bytes =@= `10:1234567890,`).
+
+test(decode_ten_bytes) :-
+ netstring_encoding(netstring(PayloadBytes), `10:1234567890,`),
+ assertion(PayloadBytes =@= `1234567890`).
+
+test(encode_nested) :-
+ InnerPayload = [0, 255],
+ PayloadGoal = netstring_encoding(netstring(InnerPayload)),
+ netstring_of(PayloadGoal, Bytes),
+ flatten([`5:2:\0`, 255, `,,`], ExpectedBytes),
+ assertion(Bytes =@= ExpectedBytes).
+
+test(decode_nested) :-
+ flatten([`5:2:\0`, 255, `,,`], Bytes),
+ PayloadGoal = netstring_encoding(netstring(InnerPayload)),
+ netstring_of(PayloadGoal, Bytes),
+ assertion(InnerPayload =@= [0, 255]).
+
+:- end_tests(spec_netstring).