about summary refs log tree commit diff stats
path: root/html/002test.cc.html
blob: c37f136f16acb3f8e2562e86259f2324e2e6f83c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 002test.cc</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v1">
<meta name="syntax" content="cpp">
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
body { font-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 1.05em; }
.cSpecial { color: #008000; }
.Constant { color: #00a0a0; }
.Comment { color: #9090ff; }
.Delimiter { color: #a04060; }
.PreProc { color: #c000c0; }
.CommentedCode { color: #6c6c6c; }
.Identifier { color: #804000; }
-->
</style>

<script type='text/javascript'>
<!--

-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment">//: A simple test harness. To create new tests define functions starting with</span>
<span class="Comment">//: 'test_'. To run all tests so defined, run:</span>
<span class="Comment">//:   $ wart test</span>
<span class="Comment">//:</span>
<span class="Comment">//: So far it seems tasteful for layers to never ever reach back to modify</span>
<span class="Comment">//: previously-defined tests. Every test is a contract once written, and should</span>
<span class="Comment">//: pass as-is if it is included, regardless of how much later layers change</span>
<span class="Comment">//: the program. Avoid writing 'temporary' tests that only work with some</span>
<span class="Comment">//: subsets of the program.</span>

<span class="Delimiter">:(before &quot;End Types&quot;)</span>
typedef void <span class="Delimiter">(</span>*test_fn<span class="Delimiter">)(</span>void<span class="Delimiter">);</span>

<span class="Delimiter">:(before &quot;End Globals&quot;)</span>
const test_fn Tests[] = <span class="Delimiter">{</span>
<span class="PreProc">  #include </span><span class="Constant">&quot;test_list&quot;</span>  <span class="Comment">// auto-generated; see makefile</span>
<span class="Delimiter">};</span>

bool Run_tests = <span class="Constant">false</span><span class="Delimiter">;</span>
bool Passed = <span class="Constant">true</span><span class="Delimiter">;</span>  <span class="Comment">// set this to false inside any test to indicate failure</span>
long Num_failures = <span class="Constant">0</span><span class="Delimiter">;</span>

<span class="PreProc">#define CHECK(X) \</span>
<span class="PreProc">  </span>if<span class="PreProc"> (!(X)) </span><span class="Delimiter">{</span><span class="PreProc"> \</span>
<span class="PreProc">    ++Num_failures</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    cerr &lt;&lt; </span><span class="Constant">&quot;</span><span class="cSpecial">\n</span><span class="Constant">F - &quot;</span><span class="PreProc"> &lt;&lt; __FUNCTION__ &lt;&lt; </span><span class="Constant">&quot;(&quot;</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">__FILE__</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">&quot;:&quot;</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">__LINE__</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">&quot;): &quot;</span><span class="PreProc"> &lt;&lt; #X &lt;&lt; </span><span class="cSpecial">'\n'</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    Passed = </span><span class="Constant">false</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    </span><span class="Identifier">return</span><span class="Delimiter">;</span><span class="PreProc">  </span><span class="Comment">/*</span><span class="Comment"> Currently we stop at the very first failure. </span><span class="Comment">*/</span><span class="PreProc"> \</span>
<span class="PreProc">  </span><span class="Delimiter">}</span>

<span class="PreProc">#define CHECK_EQ(X</span><span class="Delimiter">,</span><span class="PreProc"> Y) \</span>
<span class="PreProc">  </span>if<span class="PreProc"> ((X) != (Y)) </span><span class="Delimiter">{</span><span class="PreProc"> \</span>
<span class="PreProc">    ++Num_failures</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    cerr &lt;&lt; </span><span class="Constant">&quot;</span><span class="cSpecial">\n</span><span class="Constant">F - &quot;</span><span class="PreProc"> &lt;&lt; __FUNCTION__ &lt;&lt; </span><span class="Constant">&quot;(&quot;</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">__FILE__</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">&quot;:&quot;</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">__LINE__</span><span class="PreProc"> &lt;&lt; </span><span class="Constant">&quot;): &quot;</span><span class="PreProc"> &lt;&lt; #X &lt;&lt; </span><span class="Constant">&quot; == &quot;</span><span class="PreProc"> &lt;&lt; #Y &lt;&lt; </span><span class="cSpecial">'\n'</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    cerr &lt;&lt; </span><span class="Constant">&quot;  got &quot;</span><span class="PreProc"> &lt;&lt; (X) &lt;&lt; </span><span class="cSpecial">'\n'</span><span class="Delimiter">;</span><span class="PreProc">  </span><span class="Comment">/*</span><span class="Comment"> BEWARE: multiple eval </span><span class="Comment">*/</span><span class="PreProc"> \</span>
<span class="PreProc">    Passed = </span><span class="Constant">false</span><span class="Delimiter">;</span><span class="PreProc"> \</span>
<span class="PreProc">    </span><span class="Identifier">return</span><span class="Delimiter">;</span><span class="PreProc">  </span><span class="Comment">/*</span><span class="Comment"> Currently we stop at the very first failure. </span><span class="Comment">*/</span><span class="PreProc"> \</span>
<span class="PreProc">  </span><span class="Delimiter">}</span>

<span class="Delimiter">:(before &quot;End Setup&quot;)</span>
Passed = <span class="Constant">true</span><span class="Delimiter">;</span>

<span class="Delimiter">:(before &quot;End Commandline Parsing&quot;)</span>
if <span class="Delimiter">(</span>argc &gt; <span class="Constant">1</span> &amp;&amp; is_equal<span class="Delimiter">(</span>argv[<span class="Constant">1</span>]<span class="Delimiter">,</span> <span class="Constant">&quot;test&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span>
  Run_tests = <span class="Constant">true</span><span class="Delimiter">;</span>  --argc<span class="Delimiter">;</span>  ++argv<span class="Delimiter">;</span>  <span class="Comment">// shift 'test' out of commandline args</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(before &quot;End Main&quot;)</span>
if <span class="Delimiter">(</span>Run_tests<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  <span class="Comment">// Test Runs</span>
  <span class="Comment">// we run some tests and then exit; assume no state need be maintained afterward</span>

  <span class="Comment">// End Test Run Initialization</span>
  time_t t<span class="Delimiter">;</span> time<span class="Delimiter">(</span>&amp;t<span class="Delimiter">);</span>
  cerr &lt;&lt; <span class="Constant">&quot;C tests: &quot;</span> &lt;&lt; ctime<span class="Delimiter">(</span>&amp;t<span class="Delimiter">);</span>
  for <span class="Delimiter">(</span>size_t i=<span class="Constant">0</span><span class="Delimiter">;</span> i &lt; sizeof<span class="Delimiter">(</span>Tests<span class="Delimiter">)</span>/sizeof<span class="Delimiter">(</span>Tests[<span class="Constant">0</span>]<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="CommentedCode">//?     cerr &lt;&lt; i &lt;&lt; '\n'; //? 1</span>
    run_test<span class="Delimiter">(</span>i<span class="Delimiter">);</span>
  <span class="Delimiter">}</span>
  <span class="Comment">// End Tests</span>
  cerr &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
  if <span class="Delimiter">(</span>Num_failures &gt; <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
    cerr &lt;&lt; Num_failures &lt;&lt; <span class="Constant">&quot; failure&quot;</span>
         &lt;&lt; <span class="Delimiter">(</span>Num_failures &gt; <span class="Constant">1</span> ? <span class="Constant">&quot;s&quot;</span> : <span class="Constant">&quot;&quot;</span><span class="Delimiter">)</span>
         &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
    <span class="Identifier">return</span> <span class="Constant">1</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(code)</span>
void run_test<span class="Delimiter">(</span>size_t i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  if <span class="Delimiter">(</span>i &gt;= sizeof<span class="Delimiter">(</span>Tests<span class="Delimiter">)</span>/sizeof<span class="Delimiter">(</span>Tests[<span class="Constant">0</span>]<span class="Delimiter">))</span> <span class="Delimiter">{</span>
    cerr &lt;&lt; <span class="Constant">&quot;no test &quot;</span> &lt;&lt; i &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
    <span class="Identifier">return</span><span class="Delimiter">;</span>
  <span class="Delimiter">}</span>
  setup<span class="Delimiter">();</span>
  <span class="Comment">// End Test Setup</span>
  <span class="Delimiter">(</span>*Tests[i]<span class="Delimiter">)();</span>
  teardown<span class="Delimiter">();</span>
  if <span class="Delimiter">(</span>Passed<span class="Delimiter">)</span> cerr &lt;&lt; <span class="Constant">&quot;.&quot;</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>

bool is_integer<span class="Delimiter">(</span>const string&amp; s<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  <span class="Identifier">return</span> s<span class="Delimiter">.</span>find_first_not_of<span class="Delimiter">(</span><span class="Constant">&quot;0123456789-&quot;</span><span class="Delimiter">)</span> == string::npos<span class="Delimiter">;</span>
<span class="Delimiter">}</span>

long long int to_integer<span class="Delimiter">(</span>string n<span class="Delimiter">)</span> <span class="Delimiter">{</span>
  char* end = <span class="Constant">NULL</span><span class="Delimiter">;</span>
  <span class="Comment">// safe because string.c_str() is guaranteed to be null-terminated</span>
  long long int result = strtoll<span class="Delimiter">(</span>n<span class="Delimiter">.</span>c_str<span class="Delimiter">(),</span> &amp;end<span class="Delimiter">,</span> <span class="Comment">/*</span><span class="Comment">any base</span><span class="Comment">*/</span><span class="Constant">0</span><span class="Delimiter">);</span>
  if <span class="Delimiter">(</span>*end != <span class="cSpecial">'\0'</span><span class="Delimiter">)</span> cerr &lt;&lt; <span class="Constant">&quot;tried to convert &quot;</span> &lt;&lt; n &lt;&lt; <span class="Constant">&quot; to number</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span><span class="Delimiter">;</span>
  assert<span class="Delimiter">(</span>*end == <span class="cSpecial">'\0'</span><span class="Delimiter">);</span>
  <span class="Identifier">return</span> result<span class="Delimiter">;</span>
<span class="Delimiter">}</span>

<span class="Delimiter">:(before &quot;End Includes&quot;)</span>
<span class="PreProc">#include</span><span class="Constant">&lt;cstdlib&gt;</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
"n">i), inst.ingredients.at(i))) { raise << maybe(get(Recipe, r).name) << "can't copy '" << inst.ingredients.at(i).original_string << "' to '" << inst.products.at(i).original_string << "'; types don't match\n" << end(); goto finish_checking_instruction; } } break; } // End Primitive Recipe Checks default: { // Defined Recipe Checks // End Defined Recipe Checks } } finish_checking_instruction:; } } void test_copy_checks_reagent_count() { Hide_errors = true; run( "def main [\n" " 1:num, 2:num <- copy 34\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: too many products in '1:num, 2:num <- copy 34'\n" ); } void test_write_scalar_to_array_disallowed() { Hide_errors = true; run( "def main [\n" " 1:array:num <- copy 34\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: can't copy '34' to '1:array:num'; types don't match\n" ); } void test_write_scalar_to_array_disallowed_2() { Hide_errors = true; run( "def main [\n" " 1:num, 2:array:num <- copy 34, 35\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: can't copy '35' to '2:array:num'; types don't match\n" ); } void test_write_scalar_to_address_disallowed() { Hide_errors = true; run( "def main [\n" " 1:&:num <- copy 34\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: can't copy '34' to '1:&:num'; types don't match\n" ); } void test_write_address_to_character_disallowed() { Hide_errors = true; run( "def main [\n" " 1:&:num <- copy 12/unsafe\n" " 2:char <- copy 1:&:num\n" "]\n" ); CHECK_TRACE_CONTENTS( "error: main: can't copy '1:&:num' to '2:char'; types don't match\n" ); } void test_write_number_to_character_allowed() { run( "def main [\n" " 1:num <- copy 97\n" " 2:char <- copy 1:num\n" "]\n" ); CHECK_TRACE_COUNT("error", 0); } :(code) // types_match with some leniency bool types_coercible(reagent/*copy*/ to, reagent/*copy*/ from) { // Begin types_coercible(reagent to, reagent from) if (types_match_sub(to, from)) return true; if (is_real_mu_number(from) && is_mu_character(to)) return true; // End types_coercible Special-cases return false; } bool types_match_sub(const reagent& to, const reagent& from) { // to sidestep type-checking, use /unsafe in the source. // this will be highlighted in red inside vim. just for setting up some tests. if (is_unsafe(from)) return true; if (is_literal(from)) { if (is_mu_array(to)) return false; // End Matching Types For Literal(to) if (!to.type) return false; // allow writing null to any address if (is_mu_address(to)) return from.name == "null"; return size_of(to) == 1; // literals are always scalars } return types_strictly_match_sub(to, from); } // variant for others to call bool types_match(reagent/*copy*/ to, reagent/*copy*/ from) { // Begin types_match(reagent to, reagent from) return types_match_sub(to, from); } //: copy arguments for later layers bool types_strictly_match_sub(const reagent& to, const reagent& from) { if (to.type == NULL) return false; // error if (is_literal(from) && to.type->value == Number_type_ordinal) return true; // to sidestep type-checking, use /unsafe in the source. // this will be highlighted in red inside vim. just for setting up some tests. if (is_unsafe(from)) return true; // '_' never raises type error if (is_dummy(to)) return true; if (!to.type) return !from.type; return types_strictly_match(to.type, from.type); } // variant for others to call bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) { // Begin types_strictly_match(reagent to, reagent from) return types_strictly_match_sub(to, from); } bool types_strictly_match(const type_tree* to, const type_tree* from) { if (from == to) return true; if (!to) return false; if (!from) return to->atom && to->value == 0; if (from->atom != to->atom) return false; if (from->atom) { if (from->value == -1) return from->name == to->name; return from->value == to->value; } if (types_strictly_match(to->left, from->left) && types_strictly_match(to->right, from->right)) return true; // fallback: (x) == x if (to->right == NULL && types_strictly_match(to->left, from)) return true; if (from->right == NULL && types_strictly_match(to, from->left)) return true; return false; } void test_unknown_type_does_not_match_unknown_type() { reagent a("a:foo"); reagent b("b:bar"); CHECK(!types_strictly_match(a, b)); } void test_unknown_type_matches_itself() { reagent a("a:foo"); reagent b("b:foo"); CHECK(types_strictly_match(a, b)); } void test_type_abbreviations_match_raw_types() { put(Type_abbreviations, "text", new_type_tree("address:array:character")); // a has type (address buffer (address array character)) reagent a("a:address:buffer:text"); expand_type_abbreviations(a.type); // b has type (address buffer address array character) reagent b("b:address:buffer:address:array:character"); CHECK(types_strictly_match(a, b)); delete Type_abbreviations["text"]; put(Type_abbreviations, "text", NULL); } //: helpers bool is_unsafe(const reagent& r) { return has_property(r, "unsafe"); } bool is_mu_array(reagent/*copy*/ r) { // End Preprocess is_mu_array(reagent r) return is_mu_array(r.type); } bool is_mu_array(const type_tree* type) { if (!type) return false; if (is_literal(type)) return false; if (type->atom) return false; if (!type->left->atom) { raise << "invalid type " << to_string(type) << '\n' << end(); return false; } return type->left->value == Array_type_ordinal; } bool is_mu_boolean(reagent/*copy*/ r) { // End Preprocess is_mu_boolean(reagent r) if (!r.type) return false; if (is_literal(r)) return false; if (!r.type->atom) return false; return r.type->value == Boolean_type_ordinal; } bool is_mu_number(reagent/*copy*/ r) { if (is_mu_character(r.type)) return true; // permit arithmetic on unicode code points return is_real_mu_number(r); } bool is_real_mu_number(reagent/*copy*/ r) { // End Preprocess is_mu_number(reagent r) if (!r.type) return false; if (!r.type->atom) return false; if (is_literal(r)) { return r.type->name == "literal-fractional-number" || r.type->name == "literal"; } return r.type->value == Number_type_ordinal; } bool is_mu_character(reagent/*copy*/ r) { // End Preprocess is_mu_character(reagent r) return is_mu_character(r.type); } bool is_mu_character(const type_tree* type) { if (!type) return false; if (!type->atom) return false; if (is_literal(type)) return false; return type->value == Character_type_ordinal; }