Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mp_rowhash.test.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Testing mp_rowhash.sas macro
4 @details The mp_rowhash macro generates DATA step statements that compute a
5 deterministic row-level MD5 hash. These tests exercise the versioned seed,
6 character and numeric hashing rules, variable ordering, sensitivity to
7 whitespace, and reproducibility.
8
9 <h4> SAS Macros </h4>
10 @li mp_rowhash.sas
11 @li mp_assert.sas
12 @li mp_assertdsobs.sas
13 @li mp_assertscope.sas
14
15**/
16
17%mp_assertscope(SNAPSHOT)
18
19/* test 1 - versioned seed only (no input variables) */
20data _null_;
21 length actual expected $32;
22 actual='';
23 %mp_rowhash(md5_col=actual)
24 expected=put(md5('DC HASH v2'),$hex32.);
25 call symputx('t1_actual',actual);
26 call symputx('t1_expected',expected);
27run;
28%mp_assertscope(COMPARE,ignorelist=T1_ACTUAL T1_EXPECTED)
29%mp_assert(
30 iftrue=("&t1_actual"="&t1_expected"),
31 desc=Versioned seed is returned when no variables are supplied,
32 outds=work.test_results
33)
34
35/* test 2 - single character variable reference */
36data _null_;
37 length c1 $1 actual expected $32;
38 length state digest $16 pair $32;
39 c1='A';
40 actual='';
41 %mp_rowhash(md5_col=actual, cvars=c1)
42 state=md5('DC HASH v2');
43 digest=md5(trimn(c1));
44 pair=state||digest;
45 expected=put(md5(pair),$hex32.);
46 call symputx('t2_actual',actual);
47 call symputx('t2_expected',expected);
48run;
49%mp_assert(
50 iftrue=("&t2_actual"="&t2_expected"),
51 desc=Single character variable hash matches reference construction,
52 outds=work.test_results
53)
54
55/* test 3 - single numeric variable reference */
56data _null_;
57 length actual expected $32;
58 length state digest $16 numtext $64 pair $32;
59 n1=42;
60 actual='';
61 %mp_rowhash(md5_col=actual, nvars=n1)
62 state=md5('DC HASH v2');
63 /* multiply-by-one matches the internal normalisation */
64 normal=n1*1;
65 numtext=put(normal,binary64.);
66 digest=md5(trim(numtext));
67 pair=state||digest;
68 expected=put(md5(pair),$hex32.);
69 call symputx('t3_actual',actual);
70 call symputx('t3_expected',expected);
71run;
72%mp_assert(
73 iftrue=("&t3_actual"="&t3_expected"),
74 desc=Single numeric variable hash matches reference construction,
75 outds=work.test_results
76)
77
78/* test 4 - order of cvars affects the resulting hash */
79data _null_;
80 length c1 c2 $1 h1 h2 $32;
81 c1='A';
82 c2='B';
83 h1='';
84 %mp_rowhash(md5_col=h1, cvars=c1 c2)
85 h2='';
86 %mp_rowhash(md5_col=h2, cvars=c2 c1)
87 call symputx('t4_h1',h1);
88 call symputx('t4_h2',h2);
89run;
90%mp_assert(
91 iftrue=("&t4_h1" ne "&t4_h2"),
92 desc=Order of cvars changes the resulting hash,
93 outds=work.test_results
94)
95
96/* test 5 - order of nvars affects the resulting hash */
97data _null_;
98 length h1 h2 $32;
99 n1=1;
100 n2=2;
101 h1='';
102 %mp_rowhash(md5_col=h1, nvars=n1 n2)
103 h2='';
104 %mp_rowhash(md5_col=h2, nvars=n2 n1)
105 call symputx('t5_h1',h1);
106 call symputx('t5_h2',h2);
107run;
108%mp_assert(
109 iftrue=("&t5_h1" ne "&t5_h2"),
110 desc=Order of nvars changes the resulting hash,
111 outds=work.test_results
112)
113
114/* test 6 - identical rows produce identical hashes */
115data work.test6;
116 length c $5 n 8;
117 c='ABC';
118 n=42;
119 output;
120 output;
121run;
122
123data work.test6_hashed;
124 set work.test6;
125 length hash $32;
126 %mp_rowhash(md5_col=hash, cvars=c, nvars=n)
127run;
128
129proc sql noprint;
130 select count(distinct hash) into: t6_distinct trimmed
131 from work.test6_hashed;
132quit;
133
134%mp_assert(
135 iftrue=(&t6_distinct=1),
136 desc=Identical rows produce identical hashes,
137 outds=work.test_results
138)
139
140/* test 7 - different rows produce different hashes */
141data work.test7;
142 length c $5 n 8;
143 c='ABC';
144 n=42;
145 output;
146 c='ABD';
147 n=42;
148 output;
149run;
150
151data work.test7_hashed;
152 set work.test7;
153 length hash $32;
154 %mp_rowhash(md5_col=hash, cvars=c, nvars=n)
155run;
156
157proc sql noprint;
158 select count(distinct hash) into: t7_distinct trimmed
159 from work.test7_hashed;
160quit;
161
162%mp_assert(
163 iftrue=(&t7_distinct=2),
164 desc=Different rows produce different hashes,
165 outds=work.test_results
166)
167
168/* test 8 - leading blanks in character variables are retained */
169data work.test8;
170 length c $5;
171 c=' f';
172 output;
173 c='f';
174 output;
175run;
176
177data work.test8_hashed;
178 set work.test8;
179 length hash $32;
180 %mp_rowhash(md5_col=hash, cvars=c)
181run;
182
183proc sql noprint;
184 select count(distinct hash) into: t8_distinct trimmed
185 from work.test8_hashed;
186quit;
187
188%mp_assert(
189 iftrue=(&t8_distinct=2),
190 desc=Leading blanks are retained in character hashes,
191 outds=work.test_results
192)
193
194
195