ipa/test: Add test program for IPA message reception

This patch adds tests for ipa_msg_recv(), where messages are sent
either complete or partitioned.

Sponsored-by: On-Waves ehf
diff --git a/tests/ipa_recv/ipa_recv_test.c b/tests/ipa_recv/ipa_recv_test.c
new file mode 100644
index 0000000..af87c27
--- /dev/null
+++ b/tests/ipa_recv/ipa_recv_test.c
@@ -0,0 +1,226 @@
+/* IPA receive test */
+
+/*
+ * (C) 2014 by On-Waves
+ * (C) 2014 by sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/abis/e1_input.h>
+
+#include <osmocom/abis/ipa.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+static const char *ipa_test_messages[] = {
+	"Hello IPA",
+	"A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
+	"Hello again IPA",
+	"",
+	"Next is empty",
+	NULL,
+	"Bye",
+	"Bye",
+};
+
+static void append_ipa_message(struct msgb *msg, int proto, const char *text)
+{
+	int len = 0;
+	unsigned char *l2;
+
+	if (text)
+		len = strlen(text) + 1;
+
+	msgb_put_u16(msg, len);
+	msgb_put_u8(msg, proto);
+
+	l2 = msgb_put(msg, len);
+	if (text)
+		strcpy((char *)l2, text);
+}
+
+static int receive_messages(int fd)
+{
+	struct msgb *msg;
+	char dummy;
+	int rc;
+	while (1) {
+		if (recv(fd, &dummy, 1, MSG_PEEK) < 1) {
+			rc = -EAGAIN;
+			break;
+		}
+		msg = NULL;
+		rc = ipa_msg_recv(fd, &msg);
+		if (rc == -1)
+			rc = -errno;
+		fprintf(stderr,
+			"ipa_msg_recv: %d, msg %s NULL\n",
+			rc, msg ? "!=" : "==");
+		if (rc == -EAGAIN)
+			printf( "got msg %s NULL, "
+				"returned: %s\n",
+				msg ?  "!=" : "==",
+				rc == 0 ? "EOF" :
+				rc > 0 ? "OK" :
+				strerror(-rc));
+		if (rc == 0)
+			return 0;
+		if (rc == -EAGAIN)
+			break;
+		if (rc < 0) {
+			printf("ipa_msg_recv failed with: %s\n", strerror(-rc));
+			return rc;
+		}
+		printf("got IPA message, size=%d, proto=%d, text=\"%s\"\n",
+		       rc, msg->data[2], msg->l2h);
+		msgb_free(msg);
+	};
+
+	return rc;
+}
+
+static int slurp_data(int fd) {
+	int rc;
+	char buf[256];
+	int count = 0;
+
+	do {
+		rc = recv(fd, buf, sizeof(buf), 0);
+		if (rc <= 0)
+			break;
+
+		count += rc;
+	} while (1);
+
+	return count;
+};
+
+static void test_complete_recv(void)
+{
+	int sv[2];
+	struct msgb *msg_out = msgb_alloc(4096, "msg_out");
+	int rc, i;
+
+	printf("Testing IPA recv with complete messages.\n");
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
+		err(1, "socketpair");
+
+	fcntl(sv[0], F_SETFL, O_NONBLOCK);
+
+	for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++)
+		append_ipa_message(msg_out, 200, ipa_test_messages[i]);
+
+	while (msg_out->len > 0) {
+		rc = write(sv[1], msg_out->data, msg_out->len);
+		if (rc == -1)
+			err(1, "write");
+		msgb_pull(msg_out, rc);
+	}
+
+	for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) {
+		rc = receive_messages(sv[0]);
+		if (rc == 0)
+			break;
+
+		if (rc < 0 && rc != -EAGAIN)
+			break;
+	}
+
+	rc = slurp_data(sv[0]);
+	printf("done: unread %d, unsent %d\n", rc, msg_out->len);
+
+	close(sv[1]);
+	close(sv[0]);
+
+	msgb_free(msg_out);
+}
+
+
+static void test_partial_recv(void)
+{
+	int sv[2];
+	struct msgb *msg_out = msgb_alloc(4096, "msg_out");
+	int rc, i;
+
+	printf("Testing IPA recv with partitioned messages.\n");
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
+		err(1, "socketpair");
+
+	fcntl(sv[0], F_SETFL, O_NONBLOCK);
+
+	for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++)
+		append_ipa_message(msg_out, 200, ipa_test_messages[i]);
+
+	while (msg_out->len > 0) {
+		int len = 5;
+		if (len > msg_out->len)
+			len = msg_out->len;
+		if (write(sv[1], msg_out->data, len) == -1)
+			err(1, "write");
+		msgb_pull(msg_out, len);
+
+		if (msg_out->len == 0)
+			shutdown(sv[1], SHUT_WR);
+
+		rc = receive_messages(sv[0]);
+
+		if (rc == 0)
+			break;
+
+		if (rc < 0 && rc != -EAGAIN)
+			break;
+	}
+	rc = slurp_data(sv[0]);
+	printf("done: unread %d, unsent %d\n", rc, msg_out->len);
+
+	close(sv[1]);
+	close(sv[0]);
+
+	msgb_free(msg_out);
+}
+
+static struct log_info info = {};
+
+int main(int argc, char **argv)
+{
+	osmo_init_logging(&info);
+	log_set_all_filter(osmo_stderr_target, 1);
+	log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+	printf("Testing the IPA layer.\n");
+
+	/* run the tests */
+	test_complete_recv();
+	test_partial_recv();
+
+	printf("No crashes.\n");
+	return 0;
+}