aboutsummaryrefslogtreecommitdiffhomepage
path: root/test-internals.c
diff options
context:
space:
mode:
Diffstat (limited to 'test-internals.c')
-rw-r--r--test-internals.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/test-internals.c b/test-internals.c
new file mode 100644
index 0000000..0ccd0de
--- /dev/null
+++ b/test-internals.c
@@ -0,0 +1,145 @@
+
+/* Internal tests that cannot be run against the library without unnecessary export of symbols.
+ Copyright (c) 2017, Perlbotics / see LICENSE.txt
+ */
+
+
+#include "zxcvbn.h"
+/* need access to internals for testing; not linked against library, just inlined */
+#include "zxcvbn.c"
+
+
+/**********************************************************************************
+ * Internal checks: Validate if the first element of each group is sorted in
+ * ascending order. CharBinSearch(...) fails otherwise.
+ * Returns 0 on success.
+ * Returns element index [1..] of first error entry that is less than previous one.
+ */
+static int check_order(const uint8_t *Ents, unsigned int NumEnts, unsigned int SizeEnts) {
+ const uint8_t *last;
+ unsigned int i;
+
+ if (!Ents) return 0;
+ last = 0;
+
+ for (i = 0; i < NumEnts; ++i, Ents += SizeEnts) {
+ if (last && *last > *Ents) {
+ unsigned int j;
+
+ printf("Entry#%d [%d]: '%c' > '%c' (0x%02X > 0x%02X)\n A: ", i, i * SizeEnts, *last, *Ents, *last, *Ents);
+ for (j = 0; j < SizeEnts; ++j) {
+ printf("'%c' ", last[j] ? last[j] : ' ');
+ }
+ printf("\n >\n B: ");
+ for (j = 0; j < SizeEnts; ++j) {
+ printf("'%c' ", Ents[j] ? Ents[j] : ' ');
+ }
+ printf("\n");
+
+ return i;
+ }
+ last = Ents;
+ }
+
+ return 0; /* cannot be a misordered position; first possible one: 1 */
+}
+
+/**********************************************************************************
+ * Internal checks: Checks keyboard data integrity.
+ * Returns 0 on succes.
+ * Otherwise, number of errors are reported.
+ */
+static unsigned int selftest_keyboards() {
+ unsigned int errors;
+ const Keyboard_t *k;
+ unsigned int Indx;
+ const uint8_t *keys;
+ int i,j,errpos, blanks;
+
+ errors = 0;
+ for(k = Keyboards, Indx = 0; Indx < (sizeof Keyboards / sizeof Keyboards[0]); ++Indx, ++k) {
+ /* if one of these assrtion fails, we cannot use binary search algorithm */
+ if (k->Shifts && strlen((const char*)k->Shifts) % 2 == 1) {
+ printf("ERROR: Keyboard[%d]: Shifts-string has odd number of entries.\n", Indx);
+ ++errors;
+ }
+
+ if ( (errpos = check_order(k->Shifts, k->NumShift, 2)) ) {
+ printf("ERROR: Keyboard[%d]: Error above in sort order of Shifts-string near item #%d.\n", Indx, errpos);
+ ++errors;
+ }
+
+ if ( (errpos = check_order(k->Keys, k->NumKeys, k->NumNear)) ) {
+ printf("ERROR: Keyboard[%d]: Error above in sort order of keyboard-entries! Problem near item #%d.\n", Indx, errpos);
+ ++errors;
+ continue;
+ }
+
+ /* For each key (c0), check all its neighbours (ci):
+ * Does the neighbour key (c1==ci) have an entry (cx) in the opposite direction [rev_idx]
+ * pointing back to the current key c0?
+ * c0: ...ci.. --> c1: ..cx... --> cx==c0?
+ */
+ keys = k->Keys;
+ blanks = 0;
+ for(i = 0; i < k->NumKeys; ++i) {
+ uint8_t c0;
+ c0 = keys[i * k->NumNear];
+
+ for (j = 0; j < k->NumNear - 1; ++j) {
+ const uint8_t *c1;
+ uint8_t ci, cx;
+ int rev_idx;
+
+ /* rev_idx: reverse/opposite index to find opposite key location [0..6|8] --> [0..6|8] */
+ rev_idx = (j + (k->NumNear - 1)/2) % (k->NumNear - 1);
+ ci = keys[i * k->NumNear + j + 1];
+
+ if (ci) {
+ c1 = CharBinSearch(ci, keys, k->NumKeys, k->NumNear);
+ if (c1) {
+ if (ci == c0) {
+ printf("ERROR: Keyboard[%d]: recursion - key '%c' cannot be its own neighbour!\n", Indx, *c1);
+ ++errors;
+ } else {
+ if ( (cx = c1[ 1 + rev_idx ]) ) {
+ if ( cx != c0 ) {
+ printf("ERROR: Keyboard[%d]: c0='%c':...(ci=%c)... -> c1='%c':...(cx=%c)... --!--> c0='%c':... \n",
+ Indx, c0, ci, *c1, cx, c0);
+ ++errors;
+ }
+ } else { /* reverse pointer is NULL */
+ printf("ERROR: Keyboard[%d]: reverse entry missing in row c1='%c'[%d] pointing back to c0='%c'!\n", Indx, *c1, 1+rev_idx, c0);
+ ++errors;
+ }
+ }
+ } else {
+ printf("ERROR: Keyboard[%d]: no entry (neighbour list) found for src-char c1==ci='%c'\n", Indx, ci);
+ ++errors;
+ }
+ } else { /* blank neighbour key reference found */
+ ++blanks;
+ }
+ }
+ }
+ if (blanks != k->NumBlank) {
+ printf("ERROR: Keyboard[%d]: number of blank keys announced (%d) does not match number of blank keys counted (%d)!\n",
+ Indx, k->NumBlank, blanks);
+ ++errors;
+ }
+ }
+ return errors;
+}
+
+
+
+
+int main() {
+ unsigned int errors;
+
+ if( (errors = selftest_keyboards()) ){ /* currently only these */
+ printf("FAILED: [KEYBOARDS] - selftest returned %d error(s).\n", errors);
+ }
+
+ return errors ? 1 : 0;
+}