My previous message is waiting for moderation, since my patch is a big one.

On 5/12/20 10:51 PM, Geert Stappers wrote:
> On Mon, May 04, 2020 at 05:02:38PM +0200, Petr Menšík wrote:
...
> 
> I think it is great to have unittests.
> 
> 
> 
> <quote from="Harry Callahan" skipped="yes"/>
> 
> 
> 
>  
>> 1. https://github.com/InfrastructureServices/dnsmasq-tests
>> 2. https://github.com/beakerlib/beakerlib
>> 3. https://github.com/InfrastructureServices/dnsmasq/tree/unittests/tests
> 
> Be aware that I'm a "sysadmin",  not a programmer ...
> 
> 
>> diff --git a/Makefile b/Makefile
>> index 78e25f0..e390745 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -24,7 +24,7 @@ MANDIR        = $(PREFIX)/share/man
>>  LOCALEDIR     = $(PREFIX)/share/locale
>>  BUILDDIR      = $(SRC)
>>  DESTDIR       = 
>> -CFLAGS        = -Wall -W -O2
>> +CFLAGS        = -Wall -W -O2 -ggdb
> 
> How does that effect the regular builds?
It was added by mistake, thank you for pointing to it.
>>  LDFLAGS       = 
>>  COPTS         = 
>>  RPM_OPT_FLAGS = 
> 
>       ....
> 
> To be continued ...
OK, patch cleaned up, should apply without any warning now.
> 

-- 
Petr Menšík
Software Engineer
Red Hat, http://www.redhat.com/
email: pemen...@redhat.com
PGP: DFCF908DB7C87E8E529925BC4931CA5B6C9FC5CB
>From 5a6b0fdd66d2e6854f1a08e900a6db5d2e7b2d09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com>
Date: Thu, 14 May 2020 16:04:49 +0200
Subject: [PATCH] Create unit tests for dnsmasq
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Squashed commit of the following:

commit 966f5c3b62548e51c39a77f5883c786bd929fe7e
Author: Petr Menšík <pemen...@redhat.com>
Date:   Thu May 14 15:53:47 2020 +0200

    Remove trailing spaces

commit 4a1dfb40f5d15097d5dd1d9e37412c4a05427720
Author: Petr Menšík <pemen...@redhat.com>
Date:   Thu May 14 14:41:19 2020 +0200

    Make check target work even without kyua

    Debian still does not have kyua package in repositories. Make simpler
    custom runner in Makefile to run unit tests without it.

commit 8c3dea491eb60d177718998443225884bf771099
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 11 19:40:52 2020 +0200

    Add check target to main Makefile

    It is common to use check target. Make it available for dnsmasq.

commit fc895df9b265d899177dda6901c8ef323a501c27
Author: Petr Menšík <pemen...@redhat.com>
Date:   Thu May 7 14:41:36 2020 +0200

    Add makefile rule to first build dnsmasq

    If dnsmasq sources are changed, keep dnsmasq up-to-date.

commit 2082bda8a1a2255bb83dc19e66fe08ecf7b2d3b4
Author: Petr Menšík <pemen...@redhat.com>
Date:   Thu May 7 14:40:40 2020 +0200

    Stop passing CONFIG_ADDR to find_config

    Fixes also error in alt-host check, which fixes test for correct
    regression fix.

commit 84e5823ad370d151739933f3d9488385d8655922
Author: Petr Menšík <pemen...@redhat.com>
Date:   Thu May 7 13:54:18 2020 +0200

    Add speed test for searching dhcp entries

    Allows simple customization in number of repeats and hosts count.

commit ba4d72ac1e96d4c9775971461e3ccc9836d76635
Author: Petr Menšík <pemen...@redhat.com>
Date:   Wed May 6 19:41:31 2020 +0200

    Rename functions to correlate to dhcp_config

    Rename macro to a name well describing what it does. Rename sort
    functions to dhcp_config prefix.

commit b0472dbcfdea26a065245762f178176c461675cb
Author: Petr Menšík <pemen...@redhat.com>
Date:   Tue May 5 23:24:19 2020 +0200

    Create wildcard specific comparison for macs.

commit 6b041c9b6c7c1ad8ceb4cd99f1855be50bf5e87a
Author: Petr Menšík <pemen...@redhat.com>
Date:   Tue May 5 23:23:27 2020 +0200

    Add tagged host and wildcard hwaddr checks

    tags are now supported in dhcp-host. Ensure the most specific tag is
    used if it matches. Check also wildcard match does work.

commit d6f9530aba65cc296ed943352e1428ae91eeb515
Author: Petr Menšík <pemen...@redhat.com>
Date:   Tue May 5 23:20:25 2020 +0200

    Create comparing function for config sorting

    Prepare set of functions for comparing two configs. Allows sorting of
    dhcp-hosts lines from configuration according to their importance.

    Sorted entries would allow searching most similar record without walking
    the list again and again. Just return first entry matching criteria.

commit bc04b97874df589be00c569ae2d0db2c5211da1a
Author: Petr Menšík <pemen...@redhat.com>
Date:   Tue May 5 14:21:12 2020 +0200

    Add testing of hex parsing and handling

    It is used for mac address parsing for DHCP and in DHCP options.

commit 19ca3d8b5afe0f28b39569c6b5d8f311fb322170
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 21:36:40 2020 +0200

    Add test target using kyua

    List all tests in Kyuafile and run test. Requires kyua.

commit 429eedae1505c295522c5685ab088318081ef542
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 16:00:31 2020 +0200

    Add mixed DHCP test entries for both IPv4 and IPv6

    Ensure it works also with both ways.

commit 51167220db819ab736f38c905eee22e792bd0dbf
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 15:42:45 2020 +0200

    Merge all objs and add licenses

    Stop using workarounds for option test, use full objects from dnsmasq,
    without few exceptions.

    Updated also all files to include license headers.

commit 90fd2d1074e8d1a2e8abcd9d2fc72ca0373e2a4a
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 15:20:09 2020 +0200

    Add starting parameters to dnsmasq

    Ensures dnsmasq does not read configuration from the system.

commit 8a35fe6c455f2c262abd8d1dd3e098db6dfb7d45
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 15:04:35 2020 +0200

    Fix dhcp test

    Prepare two basic tests for ipv4 parsing and ipv6 parsing in separate
    configuration. Check mac-assigned configuration gets different hostname
    than just hostname matched entry.

commit 4005dd7877e22eea1e5ea388e298498f53a8667a
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 15:03:21 2020 +0200

    Do not optimize, fix multiple tests in one file

commit 8c5b22742b9af657ba190c4880381f48afbde706
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 11:22:47 2020 +0200

    Add rule to create cscope

commit 6ac0c66526e4b52d35ec414b86dce0f8c0843eca
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 11:22:36 2020 +0200

    Add gitignore file

commit 47a9fdb49594aae316e667c05c908961d30d7377
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 11:22:20 2020 +0200

    Create dhcp_test

    Use most of source files with exception of dnsmasq.o.
    Solve undefined symbols with fake test commands.

    Currently compiles, but crashes.

commit 356a7386859e480a74be1f38329c7283b59f7aed
Author: Petr Menšík <pemen...@redhat.com>
Date:   Mon May 4 11:22:04 2020 +0200

    Add first basic option test

commit 61878fad145e75e7d66010c53ce94ae07af03e72
Author: Petr Menšík <pemen...@redhat.com>
Date:   Sat May 2 10:28:44 2020 +0200

    Basic core for unit tests

    Have to fix yet pending unresolved symbols.
---
 Makefile            |   3 +
 tests/.gitignore    |   3 +
 tests/Kyuafile      |   6 +
 tests/Makefile      |  65 +++++
 tests/dhcp_test.c   | 582 ++++++++++++++++++++++++++++++++++++++++++++
 tests/option_test.c |  94 +++++++
 tests/test.h        |  28 +++
 tests/testcore.c    |  35 +++
 tests/testlib.c     |  85 +++++++
 9 files changed, 901 insertions(+)
 create mode 100644 tests/.gitignore
 create mode 100644 tests/Kyuafile
 create mode 100644 tests/Makefile
 create mode 100644 tests/dhcp_test.c
 create mode 100644 tests/option_test.c
 create mode 100644 tests/test.h
 create mode 100644 tests/testcore.c
 create mode 100644 tests/testlib.c

diff --git a/Makefile b/Makefile
index 78e25f0..631fa91 100644
--- a/Makefile
+++ b/Makefile
@@ -98,6 +98,9 @@ clean : mostly_clean
 	rm -f core */core
 	rm -f *~ contrib/*/*~ */*~
 
+check: all
+	$(MAKE) -C tests check
+
 install : all install-common
 
 install-common :
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..7e4b5f2
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,3 @@
+*.o
+*_test
+cscope.out
diff --git a/tests/Kyuafile b/tests/Kyuafile
new file mode 100644
index 0000000..a3289c3
--- /dev/null
+++ b/tests/Kyuafile
@@ -0,0 +1,6 @@
+# export CMOCKA_MESSAGE_OUTPUT=TAP
+syntax(2)
+test_suite('dnsmasq')
+
+tap_test_program{name='option_test'}
+tap_test_program{name='dhcp_test'}
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..be3195c
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,65 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 dated June, 1991, or
+# (at your option) version 3 dated 29 June, 2007.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Do not optimize too much. dnsmasq object files are optimized already
+# but tests need easier debugging by default
+CFLAGS=-O0 -Wall -ggdb
+SRC=../src
+CPPFLAGS=-I$(SRC)
+LIBS=-lcmocka
+
+DNSMASQ_OBJS=$(SRC)/option.o $(SRC)/log.o $(SRC)/util.o $(SRC)/poll.o \
+		  $(SRC)/cache.o $(SRC)/network.o $(SRC)/netlink.o $(SRC)/dhcp-common.o \
+		  $(SRC)/dhcp.o $(SRC)/dhcp6.o $(SRC)/rfc2131.o $(SRC)/rfc3315.o \
+		  $(SRC)/rfc1035.o $(SRC)/forward.o $(SRC)/loop.o $(SRC)/lease.o \
+		  $(SRC)/domain.o $(SRC)/radv.o $(SRC)/outpacket.o $(SRC)/arp.o $(SRC)/blockdata.o \
+		  $(SRC)/edns0.o $(SRC)/dump.o
+TESTCORE=testcore.o testlib.o
+TESTCORE_SRCS=test.h testcore.c testlib.c
+
+TEST_OBJS=option_test.o dhcp_test.o
+TEST_SRCS=option_test.c dhcp_test.c
+TESTS=option_test dhcp_test
+
+all: src $(TESTS)
+
+src:
+	$(MAKE) -C $(SRC)/..
+
+option_test: option_test.o $(TESTCORE) $(DNSMASQ_OBJS)
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS)
+
+dhcp_test: dhcp_test.o $(TESTCORE) $(DNSMASQ_OBJS)
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS)
+
+# Debian still does not have kyua package
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=766577
+# To simplify testing now, omit kyua
+kyua: $(TESTS)
+	CMOCKA_MESSAGE_OUTPUT=TAP kyua test
+
+test: $(TESTS)
+	for TEST in $(TESTS); do echo $$TEST:; ./$$TEST || exit 1; done
+
+check: test
+
+clean:
+	rm -f $(TESTCORE) $(TEST_OBJS) $(TESTS)
+
+cscope.out: $(TESTCORE_SRCS) $(TEST_SRCS)
+	cscope -R -b -s $(SRC)
+
+tags: cscope.out
+
+%.o: %.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
diff --git a/tests/dhcp_test.c b/tests/dhcp_test.c
new file mode 100644
index 0000000..f3d2674
--- /dev/null
+++ b/tests/dhcp_test.c
@@ -0,0 +1,582 @@
+// vim: sts=2
+/*
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* DHCP protocol definitions and configuration lookup tests.
+ *
+ * Because dnsmasq uses die() function to report failures,
+ * unsupported options cannot be tested now. */
+
+#include "test.h"
+#include <stdlib.h>
+
+#if 0
+/* dhcp-range, stored in daemon->dhcp */
+/*
+(gdb) p *context
+$23 = {
+  lease_time = 3600,
+  addr_epoch = 0,
+  netmask = {
+    s_addr = 16777215
+  },
+  broadcast = {
+    s_addr = 4279246508
+  },
+  local = {
+    s_addr = 17833644
+  },
+  router = {
+    s_addr = 17833644
+  },
+  start = {
+    s_addr = 84942508
+  },
+  end = {
+    s_addr = 504372908
+  },
+  start6 = {
+    __in6_u = {
+      __u6_addr8 = '\000' <repeats 15 times>,
+      __u6_addr16 = {0, 0, 0, 0, 0, 0, 0, 0},
+      __u6_addr32 = {0, 0, 0, 0}
+    }
+  },
+  end6 = {
+    __in6_u = {
+      __u6_addr8 = '\000' <repeats 15 times>,
+      __u6_addr16 = {0, 0, 0, 0, 0, 0, 0, 0},
+      __u6_addr32 = {0, 0, 0, 0}
+    }
+  },
+  local6 = {
+    __in6_u = {
+      __u6_addr8 = '\000' <repeats 15 times>,
+      __u6_addr16 = {0, 0, 0, 0, 0, 0, 0, 0},
+      __u6_addr32 = {0, 0, 0, 0}
+    }
+  },
+  prefix = 0,
+  if_index = 0,
+  valid = 0,
+  preferred = 0,
+  saved_valid = 0,
+  ra_time = 0,
+  ra_short_period_start = 0,
+  address_lost_time = 0,
+  template_interface = 0x0,
+  flags = 0,
+  netid = {
+    net = 0x0,
+    next = 0x0
+  },
+  filter = 0x0,
+  next = 0x0,
+  current = 0x0
+}
+*/
+
+/* Request from dhclient over IPv4 */
+/*
+(gdb) p hwaddr[0]
+$8 = 58 ':'
+(gdb) p hwaddr[1]
+$9 = 26 '\032'
+(gdb) p hwaddr[2]
+$10 = 162 '\242'
+(gdb) p hwaddr[3]
+$11 = 54 '6'
+(gdb) p hwaddr[4]
+$12 = 37 '%'
+(gdb) p hwaddr[5]
+$13 = 73 'I'
+(gdb) p hwaddr[6]
+$14 = 0 '\000'
+
+#0  find_config (configs=0x0, context=context@entry=0x55f4f6004750, clid=clid@entry=0x0, clid_len=clid_len@entry=0,
+    hwaddr=hwaddr@entry=0x55f4f60061cc ":\032\242\066%I", hw_len=6, hw_type=1, hostname=0x0, tags=0x7fffc9d51240)
+    at dhcp-common.c:382
+
+(gdb) p /x *(struct dhcp_packet *)dnsmasq_daemon->dhcp_packet.iov_base
+$18 = {
+  op = 0x2,
+  htype = 0x1,
+  hlen = 0x6,
+  hops = 0x0,
+  xid = 0x31505f32,
+  secs = 0x0,
+  flags = 0x0,
+  ciaddr = {
+    s_addr = 0x0
+  },
+  yiaddr = {
+    s_addr = 0x0
+  },
+  siaddr = {
+    s_addr = 0x0
+  },
+  giaddr = {
+    s_addr = 0x0
+  },
+  chaddr = {0x3a, 0x1a, 0xa2, 0x36, 0x25, 0x49, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+  sname = {0x0 <repeats 64 times>},
+  file = {0x0 <repeats 128 times>},
+  options = {0x63, 0x82, 0x53, 0x63, 0x35, 0x1, 0x3, 0x32, 0x4, 0xac, 0x1e, 0x10, 0x1d, 0x37, 0xd, 0x1, 0x1c, 0x2, 0x79, 0xf,
+    0x6, 0xc, 0x28, 0x29, 0x2a, 0x1a, 0x77, 0x3, 0xff, 0x0 <repeats 283 times>}
+}
+*/
+#endif
+#if 0
+  iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
+			   now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
+#endif
+#if 0
+  struct dhcp_packet pkt_data = {
+	  .op=2, .htype=1, .hlen=6, .hops=0,
+	  .xid=0x31505f32,
+	  .secs=0, .flags=0,
+	  { INADDR_ANY },
+	  { INADDR_ANY },
+	  { INADDR_ANY },
+	  { INADDR_ANY },
+	  .chaddr = {0x3a, 0x1a, 0xa2, 0x36, 0x25, 0x49, 0x0, },
+	  .sname = {0, },
+	  .file =  {0, },
+	  .options = { 0x63, 0x82, 0x53, 0x63, 0x35, 0x1, 0x3, 0x32, 0x4, 0xac, 0x1e, 0x10, 0x1d, 0x37, 0xd,
+		  0x1, 0x1c, 0x2, 0x79, 0xf, 0x6, 0xc, 0x28, 0x29, 0x2a, 0x1a, 0x77, 0x3, 0xff, 0x0, },
+  };
+  unsigned char hwaddr[] = { 58, 26, 126, 54, 37, 73, 0 };
+#endif
+
+#define ZERO_OR_RETURN(expr, d) do { \
+				      (d) = (expr); \
+				      if ((d) != 0) \
+					return (d); \
+				 } while (0)
+
+static int compare_netid(const struct dhcp_netid *f1, const struct dhcp_netid *f2)
+{
+  int d;
+  while (f1 && f2) {
+    ZERO_OR_RETURN(strcmp(f1->net, f2->net), d);
+    f1 = f1->next;
+    f2 = f2->next;
+  }
+  return ((f1 != NULL) - (f2 != NULL));
+}
+
+#if 0
+static int compare_hwaddr_wild(const struct hwaddr_config *hw1, const struct hwaddr_config *hw2)
+{
+  int d;
+  while (hw1 && hw2)
+    {
+      ZERO_OR_RETURN((hw1->wildcard_mask == 0) - (hw2->wildcard_mask == 0), d);
+      ZERO_OR_RETURN(hw1->wildcard_mask - hw2->wildcard_mask, d);
+      ZERO_OR_RETURN(hw1->hwaddr_type - hw2->hwaddr_type, d);
+      ZERO_OR_RETURN(hw1->hwaddr_len - hw2->hwaddr_len, d);
+      ZERO_OR_RETURN(memcmp(hw1->hwaddr, hw2->hwaddr, hw1->hwaddr_len), d);
+
+      hw1 = hw1->next;
+      hw2 = hw2->next;
+  }
+  return ((hw1 != NULL) - (hw2 != NULL));
+}
+
+static int compare_hwaddr_nowild(const struct hwaddr_config *hw1, const struct hwaddr_config *hw2)
+{
+  int d;
+  while (hw1 && hw2)
+    {
+      ZERO_OR_RETURN((hw1->wildcard_mask == 0) - (hw2->wildcard_mask == 0), d);
+      if (hw1->wildcard_mask != 0)
+	return 0;
+      ZERO_OR_RETURN(hw1->hwaddr_type - hw2->hwaddr_type, d);
+      ZERO_OR_RETURN(hw1->hwaddr_len - hw2->hwaddr_len, d);
+      ZERO_OR_RETURN(memcmp(hw1->hwaddr, hw2->hwaddr, hw1->hwaddr_len), d);
+
+      hw1 = hw1->next;
+      hw2 = hw2->next;
+  }
+  return ((hw1 != NULL) - (hw2 != NULL));
+}
+#endif
+
+static int compare_hwaddr(const struct hwaddr_config *hw1, const struct hwaddr_config *hw2)
+{
+  int d;
+  while (hw1 && hw2)
+    {
+      ZERO_OR_RETURN((hw1->wildcard_mask == 0) - (hw2->wildcard_mask == 0), d);
+      ZERO_OR_RETURN(hw1->hwaddr_type - hw2->hwaddr_type, d);
+      ZERO_OR_RETURN(hw1->hwaddr_len - hw2->hwaddr_len, d);
+      ZERO_OR_RETURN(memcmp(hw1->hwaddr, hw2->hwaddr, hw1->hwaddr_len), d);
+
+      hw1 = hw1->next;
+      hw2 = hw2->next;
+  }
+  return ((hw1 != NULL) - (hw2 != NULL));
+}
+
+static int compare_dhcp_config(const struct dhcp_config *c1, const struct dhcp_config *c2)
+{
+  int d;
+
+  ZERO_OR_RETURN(compare_netid(c1->filter, c2->filter), d);
+  ZERO_OR_RETURN( ((c1->flags & CONFIG_CLID) != 0) - ((c2->flags & CONFIG_CLID) != 0), d);
+  ZERO_OR_RETURN(c1->clid_len - c2->clid_len, d);
+  if (c1->clid_len > 0)
+    {
+      ZERO_OR_RETURN(memcmp(c1->clid, c2->clid, c1->clid_len), d);
+    }
+
+  ZERO_OR_RETURN(compare_hwaddr(c1->hwaddr, c2->hwaddr), d);
+  ZERO_OR_RETURN(((c1->flags & CONFIG_NAME) != 0) - ((c2->flags & CONFIG_NAME) != 0), d);
+  if ((c1->flags & CONFIG_NAME) != 0)
+    {
+      ZERO_OR_RETURN(strcmp(c1->hostname, c2->hostname), d);
+    }
+
+  return 0;
+}
+
+#undef ZERO_OR_RETURN
+
+static int compare_config_ptr(const void *c1, const void *c2)
+{
+  return -compare_dhcp_config(*((const struct dhcp_config **)c1),
+			     *((const struct dhcp_config **)c2));
+}
+
+static size_t dhcp_config_count(const struct dhcp_config *cfg)
+{
+  size_t count = 0;
+  for ( ; cfg; cfg = cfg->next)
+    ++count;
+  return count;
+}
+
+static void dhcp_config_sort_array(struct dhcp_config **configs,
+			       struct dhcp_config **cfga, size_t count)
+{
+  struct dhcp_config *cfg = NULL;
+  size_t i=0;
+
+  for (i = 0, cfg= *configs; cfg && i < count; i++, cfg = cfg->next)
+    cfga[i] = cfg;
+
+  qsort(cfga, count, sizeof(cfga[0]), &compare_config_ptr);
+
+  *configs = cfg = cfga[0];
+  for (i=1; i<count; i++, cfg = cfg->next)
+    cfg->next = cfga[i];
+  cfg->next = NULL;
+}
+
+static void dhcp_config_sort(struct dhcp_config **configs)
+{
+  struct dhcp_config **cfga;
+  size_t count = 0;
+
+  count = dhcp_config_count(*configs);
+  if (count == 0)
+    return;
+
+  cfga = safe_malloc(count*sizeof(struct dhcp_config));
+  memset(cfga, 0, count*sizeof(*cfga));
+  dhcp_config_sort_array(configs, cfga, count);
+
+  free(cfga);
+}
+
+#if 0
+#define CFG_TYPE(type) ,(type)
+#else
+/* This is a hack to check original code without config flags */
+#define CFG_TYPE(type)
+#endif
+
+static void test_dhcp_ipv4(void **state)
+{
+  char *argv[] = {
+	  ARGV_START,
+	  "--dhcp-range=192.168.1.5,192.168.1.10,10",
+	  "--dhcp-host=192.168.1.13,only-host",
+	  "--dhcp-host=11:22:33:44:55:66,192.168.1.12,mac-host",
+	  "--dhcp-host=tag:eth0,11:22:33:44:55:66,192.168.1.14,tag-host",
+  };
+
+  unsigned char *clid = NULL;
+  int clid_len = 0;
+  struct dhcp_context *context;
+  struct dhcp_config *config = NULL;
+  struct hwaddr_config defined_client = {
+	6, 1, { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x0 }, 0, NULL,
+  };
+  struct hwaddr_config undefined_client = {
+	6, 1, { 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x0 }, 0, NULL,
+  };
+  struct dhcp_netid eth0 = { "eth0", NULL };
+
+  (void) state;
+
+  testcore_main(ARRAY_SIZE(argv), argv);
+
+  context = daemon->dhcp;
+
+  assert_true(context);
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, &eth0 CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "tag-host");
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "mac-host");
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_false(config);
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "only-host", NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "only-host");
+  assert_false(config->hwaddr);
+}
+
+static void test_dhcp_ipv6(void **state)
+{
+  char *argv[] = {
+	  ARGV_START,
+	  "--dhcp-range=fd27:807d:181c:fb81::d000,fd27:807d:181c:fb81::dfff,64,10",
+	  "--dhcp-host=11:22:33:44:55:66,[fd27:807d:181c:fb81::d12],mac-host",
+	  "--dhcp-host=tag:eth0,11:22:33:44:55:66,[fd27:807d:181c:fb81::d12],tag-host",
+	  "--dhcp-host=[fd27:807d:181c:fb81::d13],only-host",
+  };
+
+  unsigned char *clid = NULL;
+  int clid_len = 0;
+  struct dhcp_context *context;
+  struct dhcp_config *config = NULL;
+  struct hwaddr_config defined_client = {
+	6, 1, { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x0 }, 0, NULL,
+  };
+  struct hwaddr_config undefined_client = {
+	6, 1, { 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x0 }, 0, NULL,
+  };
+  struct dhcp_netid eth0 = { "eth0", NULL };
+
+  testcore_main(ARRAY_SIZE(argv), argv);
+
+  context = daemon->dhcp6;
+
+  assert_true(context);
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, &eth0 CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "tag-host");
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "mac-host");
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_false(config);
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "only-host", NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "only-host");
+  assert_false(config->hwaddr);
+}
+
+static void test_dhcp_mixed(void **state)
+{
+  char *argv[] = {
+	  ARGV_START,
+	  "--dhcp-range=192.168.1.5,192.168.1.10,10",
+	  "--dhcp-host=11:22:33:44:55:66,192.168.1.12,mac-host4",
+	  "--dhcp-host=192.168.1.13,only-host",
+	  "--dhcp-range=fd27:807d:181c:fb81::d000,fd27:807d:181c:fb81::dfff,64,10",
+	  "--dhcp-host=11:22:33:44:55:66,[fd27:807d:181c:fb81::d12],mac-host6",
+	  "--dhcp-host=tag:eth0,11:22:33:44:55:66,192.168.1.14,[fd27:807d:181c:fb81::d12],tag-host",
+	  "--dhcp-host=[fd27:807d:181c:fb81::d13],only-host",
+	  "--dhcp-host=tag:eth0, 66:55:44:33:22:11, [fd27:807d:181c:fb81::d12], alt-host",
+	  "--dhcp-host=de:ad:*:*:be:ef, [fd27:807d:181c:fb81::d15], wildmac-host",
+  };
+  unsigned char *clid = NULL;
+  int clid_len = 0;
+  struct dhcp_config *config = NULL;
+  struct hwaddr_config defined_client = {
+	6, 1, { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x0 }, 0, NULL,
+  };
+  struct hwaddr_config undefined_client = {
+	6, 1, { 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x0 }, 0, NULL,
+  };
+  struct hwaddr_config alt = {
+	6, 1, { 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0 }, 0, NULL,
+  };
+  struct hwaddr_config wildmac = {
+	6, 1, { 0xde, 0xad, 0x44, 0x33, 0xbe, 0xef, 0x0 }, 0, NULL,
+  };
+  struct dhcp_netid eth0 = { "eth0", NULL };
+
+  testcore_main(ARRAY_SIZE(argv), argv);
+
+  assert_true(daemon->dhcp);
+  assert_true(daemon->dhcp6);
+
+  dhcp_config_sort(&daemon->dhcp_conf);
+
+  config = find_config(daemon->dhcp_conf, daemon->dhcp, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "mac-host4");
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       defined_client.hwaddr, defined_client.hwaddr_len,
+		       defined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR6));
+  assert_true(config);
+  assert_string_equal(config->hostname, "mac-host6");
+  config = find_config(daemon->dhcp_conf, daemon->dhcp, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+  assert_false(config);
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR6));
+  assert_false(config);
+  config = find_config(daemon->dhcp_conf, daemon->dhcp, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "only-host", NULL CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "only-host");
+  assert_false(config->hwaddr);
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "only-host", NULL CFG_TYPE(CONFIG_ADDR6));
+  assert_true(config);
+  assert_string_equal(config->hostname, "only-host");
+  assert_false(config->hwaddr);
+
+  config = find_config(daemon->dhcp_conf, daemon->dhcp, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "tag-host", &eth0 CFG_TYPE(CONFIG_ADDR));
+  assert_true(config);
+  assert_string_equal(config->hostname, "tag-host");
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       undefined_client.hwaddr, undefined_client.hwaddr_len,
+		       undefined_client.hwaddr_type, "tag-host", &eth0 CFG_TYPE(CONFIG_ADDR6));
+  assert_true(config);
+  assert_string_equal(config->hostname, "tag-host");
+
+  /* Intentionally search wrong address type */
+  config = find_config(daemon->dhcp_conf, daemon->dhcp, clid, clid_len,
+		       alt.hwaddr, alt.hwaddr_len,
+		       alt.hwaddr_type, NULL, &eth0 CFG_TYPE(CONFIG_ADDR));
+  assert_null(config);
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       alt.hwaddr, alt.hwaddr_len,
+		       alt.hwaddr_type, NULL, &eth0 CFG_TYPE(CONFIG_ADDR));
+  assert_non_null(config);
+  assert_string_equal(config->hostname, "alt-host");
+
+  config = find_config(daemon->dhcp_conf, daemon->dhcp6, clid, clid_len,
+		       wildmac.hwaddr, wildmac.hwaddr_len,
+		       wildmac.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR6));
+  assert_non_null(config);
+  assert_string_equal(config->hostname, "wildmac-host");
+}
+
+static char **argv_test_hosts(unsigned int hosts_count, int *done_count)
+{
+  char *argv_start[] = { ARGV_START, };
+  size_t start_items = ARRAY_SIZE(argv_start);
+  unsigned int i;
+  char template[128];
+  char **argv;
+  unsigned int hi, lo;
+
+  argv= safe_malloc(sizeof(argv_start) + sizeof(char *)*hosts_count+1);
+  memcpy(argv,argv_start, sizeof(argv_start));
+
+  for (i=0; i<hosts_count; i++) {
+    hi = i/255;
+    lo = i%255;
+    snprintf(template, sizeof(template),
+      "--dhcp-host=11:22:33:44:%02x:%02x,192.168.%d.%d,only-host%03d",
+      hi, lo, hi, lo, i);
+    argv[start_items+i] = strdup(template);
+  }
+
+  argv[start_items+i] = NULL;
+  if (done_count)
+    *done_count = start_items+i;
+  return argv;
+}
+
+static void test_dhcp_speedtest(void **state)
+{
+  size_t hosts_count = 200;
+  size_t repeats = 1000;
+  size_t host, repeat;
+  char **argv;
+  int argc;
+  struct hwaddr_config conf_template = {
+	6, 1, { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x0 }, 0, NULL,
+  };
+  struct dhcp_config *config;
+
+  if (getenv("SPEEDTEST_HOSTS"))
+    {
+      hosts_count = strtoul(getenv("SPEEDTEST_HOSTS"), NULL, 10);
+    }
+  if (getenv("SPEEDTEST_REPEATS"))
+    {
+      repeats = strtoul(getenv("SPEEDTEST_REPEATS"), NULL, 10);
+    }
+  argv = argv_test_hosts(hosts_count, &argc);
+  testcore_main(argc, argv);
+
+  for (repeat=0; repeat<repeats; repeat++) {
+    for (host=0; host<hosts_count; host++) {
+      conf_template.hwaddr[4] = (unsigned char) (host/255);
+      conf_template.hwaddr[5] = (unsigned char) (host%255);
+
+      config = find_config(daemon->dhcp_conf, daemon->dhcp, NULL, 0,
+			  conf_template.hwaddr, conf_template.hwaddr_len,
+			  conf_template.hwaddr_type, NULL, NULL CFG_TYPE(CONFIG_ADDR));
+      assert_non_null(config);
+    }
+  }
+}
+
+int main(int argc, char *argv[])
+{
+  const struct CMUnitTest tests[] = {
+    cmocka_unit_test(test_dhcp_ipv4),
+    cmocka_unit_test(test_dhcp_ipv6),
+    cmocka_unit_test(test_dhcp_mixed),
+    cmocka_unit_test(test_dhcp_speedtest),
+  };
+
+  return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/option_test.c b/tests/option_test.c
new file mode 100644
index 0000000..1d77b61
--- /dev/null
+++ b/tests/option_test.c
@@ -0,0 +1,94 @@
+// vim: sts=2
+/*
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/* Basic checks for generic options */
+#include "test.h"
+
+/* Because dnsmasq uses die() function to report failures,
+ * unsupported options cannot be tested now. */
+
+static void test_option(void **state)
+{
+  char *argv[] = {
+	  ARGV_START,
+	  "--user=dnsmasq",
+	  "--group=dnsmasq",
+	  "--domain=test",
+	  "--log-queries",
+  };
+  (void) state;
+
+  testcore_main(ARRAY_SIZE(argv), argv);
+  assert_string_equal(daemon->username, "dnsmasq");
+  assert_string_equal(daemon->groupname, "dnsmasq");
+  assert_true(option_bool(OPT_LOG));
+  assert_false(option_bool(OPT_EXTRALOG));
+}
+
+/** Test parsing hexadecimal mac addresses and wildcard compare. */
+static void test_hex(void **status)
+{
+  char hex1[] = "7-11:22:33:44:55:66";
+  char invalid2[] = "hello";
+  char hex3[] = "11:22:*";
+  char hex4[] = "11:*:33:*:55:*";
+  unsigned char data1[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+  unsigned char out[16];
+  int r;
+  unsigned int mask=0;
+  int type=0;
+
+  (void)status;
+
+  memset(out, 0, sizeof(out));
+  r = parse_hex(hex1, out, sizeof(out), &mask, &type);
+  assert_int_equal(r, 6);
+  assert_memory_equal(data1, out, sizeof(data1));
+  assert_int_equal(mask, 0);
+  assert_int_equal(type, 7);
+
+  memset(out, 0, sizeof(out));
+  r = parse_hex(invalid2, out, sizeof(out), NULL, NULL);
+  assert_int_equal(r, -1);
+
+  memset(out, 0, sizeof(out));
+  r = parse_hex(hex3, out, sizeof(out), &mask, &type);
+  assert_int_equal(r, 3);
+  assert_memory_equal(data1, out, 2);
+  assert_int_equal(mask, 0x01);
+  assert_int_equal(type, 0);
+  r = memcmp_masked(out, data1, r, mask);
+  assert_int_equal(r, 3);
+
+  memset(out, 0, sizeof(out));
+  r = parse_hex(hex4, out, sizeof(out), &mask, &type);
+  assert_int_equal(r, 6);
+  assert_int_equal(mask, 0x15);
+  assert_int_equal(type, 0);
+  r = memcmp_masked(out, data1, r, mask);
+  assert_int_equal(r, 4);
+
+  assert_string_equal("11:22:33:44:55:66", print_mac((char *)out, data1, sizeof(data1)));
+}
+
+int main(int argc, char *argv[])
+{
+  const struct CMUnitTest tests[] = {
+    cmocka_unit_test(test_option),
+    cmocka_unit_test(test_hex),
+  };
+
+  return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/test.h b/tests/test.h
new file mode 100644
index 0000000..cc21113
--- /dev/null
+++ b/tests/test.h
@@ -0,0 +1,28 @@
+// vim: sts=2
+/*
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "dnsmasq.h"
+
+#include <setjmp.h>
+#include <cmocka.h>
+
+
+#define ARRAY_SIZE(a)  (sizeof(a)/sizeof((a)[0]))
+#define ARGV_START "dnsmasq", "-C", "/dev/null"
+
+void testcore_main(int argc, char **argv);
diff --git a/tests/testcore.c b/tests/testcore.c
new file mode 100644
index 0000000..48cd7ae
--- /dev/null
+++ b/tests/testcore.c
@@ -0,0 +1,35 @@
+// vim: sts=2
+#include "test.h"
+#include <unistd.h>
+
+struct daemon *daemon = NULL;
+
+/* basic initialization taken from dnsmasq.c */
+void testcore_main(int argc, char **argv)
+{
+  rand_init();
+
+  if (daemon)
+    free(daemon);
+  optind=1;
+  read_opts(argc, argv, "unittest");
+
+#ifdef HAVE_LINUX_NETWORK
+  daemon->kernel_version = kernel_version();
+#endif
+
+  if (daemon->edns_pktsz < PACKETSZ)
+    daemon->edns_pktsz = PACKETSZ;
+
+  /* Min buffer size: we check after adding each record, so there must be
+     memory for the largest packet, and the largest record so the
+     min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
+     This might be increased is EDNS packet size if greater than the minimum. */
+  daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
+  daemon->packet = safe_malloc(daemon->packet_buff_sz);
+
+  daemon->addrbuff = safe_malloc(ADDRSTRLEN);
+  if (option_bool(OPT_EXTRALOG))
+    daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
+
+}
diff --git a/tests/testlib.c b/tests/testlib.c
new file mode 100644
index 0000000..2bdd3c5
--- /dev/null
+++ b/tests/testlib.c
@@ -0,0 +1,85 @@
+// vim: sts=2
+/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Generic required calls, faking original functions without functional body.
+ */
+#include "dnsmasq.h"
+
+/* inotify.c */
+#ifdef HAVE_INOTIFY
+void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
+{}
+#endif
+
+/* dnsmasq.c */
+void send_event(int fd, int event, int data, char *msg)
+{}
+void queue_event(int event)
+{}
+int icmp_ping(struct in_addr addr)
+{
+  return 0;
+}
+void send_alarm(time_t event, time_t now)
+{}
+int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
+{
+  return 0;
+}
+
+/* ipset.c / tables.c */
+int add_to_ipset(const char *setname, const union all_addr *ipaddr, int flags, int remove)
+{
+  return -1;
+}
+
+/* rrfilter.c */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode)
+{
+  return plen;
+}
+
+/* auth.c */
+size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
+		   int local_query, int do_bit, int have_pseudoheader)
+{
+  return 0;
+}
+int in_zone(struct auth_zone *zone, char *name, char **cut)
+{
+  return 0;
+}
+
+/* slaac.c */
+time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
+{
+  return 0;
+}
+
+void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
+{}
+void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
+{}
+
+/* helper.c */
+#ifdef HAVE_SCRIPT
+void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
+{}
+void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
+{}
+#endif
-- 
2.21.1

_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to