diff --git a/Makefile.am b/Makefile.am index 07cf8166..a6996a06 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = gdbus gatchat include plugins drivers src doc +SUBDIRS = gdbus gatchat include plugins drivers src unit doc DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles diff --git a/configure.ac b/configure.ac index 9387c30c..258eb5fc 100644 --- a/configure.ac +++ b/configure.ac @@ -87,4 +87,5 @@ AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no") AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile include/Makefile include/version.h src/Makefile - plugins/Makefile drivers/Makefile doc/Makefile) + plugins/Makefile drivers/Makefile doc/Makefile + unit/Makefile) diff --git a/unit/Makefile.am b/unit/Makefile.am new file mode 100644 index 00000000..cf64e1ef --- /dev/null +++ b/unit/Makefile.am @@ -0,0 +1,27 @@ + +noinst_PROGRAMS = test-util test-common test-sms + +test_util_SOURCES = test-util.c ../src/util.c + +test_util_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ -ldl + +test_util_LDFLAGS = -Wl,--export-dynamic + +test_common_SOURCES = test-common.c ../src/common.c + +test_common_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ -ldl + +test_common_LDFLAGS = -Wl,--export-dynamic + +test_sms_SOURCES = test-sms.c ../src/util.c ../src/smsutil.c + +test_sms_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ -ldl + +test_sms_LDFLAGS = -Wl,--export-dynamic + +AM_CFLAGS = @GTHREAD_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@ \ + -DOFONO_API_SUBJECT_TO_CHANGE + +INCLUDES = -I$(top_builddir)/include -I$(top_builddir) -I$(top_builddir)/src + +MAINTAINERCLEANFILES = Makefile.in diff --git a/unit/test-common.c b/unit/test-common.c new file mode 100644 index 00000000..5c753d3b --- /dev/null +++ b/unit/test-common.c @@ -0,0 +1,142 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include "driver.h" +#include "common.h" + +static const char *invalid_strings[] = { + "33", + "*", + "**", + "***", + "*****", + "******", + "#", + "##", + "###", + "####", + "#####", + "*#", + "**#", + "****#", + "*****#", + "**#", + "*#", + "##", + "*04*98*0000*00000*00000#", + NULL +}; + +static void test_invalid() +{ + char *sc; + char *sia; + char *sib; + char *sic; + char *dn; + int type; + + char *str; + int i; + gboolean ret; + + for (i = 0; invalid_strings[i]; i++) { + if (g_test_verbose()) + g_print("%s...\n", invalid_strings[i]); + + str = strdup(invalid_strings[i]); + + ret = parse_ss_control_string(str, &type, &sc, + &sia, &sib, &sic, &dn); + + g_assert(ret == FALSE); + + free(str); + } +} + +static const char *valid_strings[] = { + "*31#", + "*31#+55555", + "#31#", + "#31#+55555", + "*21*+55555*10*20#", + "*21*+55555*10#", + "*21**20#", + "*21*+55555#", + "*21**10*20#", + "*21**10#", + "*21***20#", + "*21#", + "**21#", + "*#21#", + "#21#", + "##21#", + NULL +}; + +static void test_valid() +{ + char *sc; + char *sia; + char *sib; + char *sic; + char *dn; + int type; + gboolean ret; + + char *str; + int i; + + for (i = 0; valid_strings[i]; i++) { + if (g_test_verbose()) + g_print("%s...", valid_strings[i]); + + str = strdup(valid_strings[i]); + + ret = parse_ss_control_string(str, &type, &sc, + &sia, &sib, &sic, &dn); + + g_assert(ret == TRUE); + + if (g_test_verbose()) + g_print("parsed as: %d, %s, %s, %s, %s, %s\n", + type, sc, sia, sib, sic, dn); + + free(str); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/testutil/Invalid", test_invalid); + g_test_add_func("/testutil/Valid", test_valid); + + return g_test_run(); +} diff --git a/unit/test-sms.c b/unit/test-sms.c new file mode 100644 index 00000000..c9a5f8a6 --- /dev/null +++ b/unit/test-sms.c @@ -0,0 +1,419 @@ +/* + * oFono - GSM Telephony Stack for Linux + * + * Copyright (C) 2008-2009 Intel Corporation. All rights reserved. + * + * 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; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include + +#include "util.h" +#include "smsutil.h" + +static const char *simple_deliver = "07911326040000F0" + "040B911346610089F60000208062917314480CC8F71D14969741F977FD07"; +static const char *alnum_sender = "0791447758100650" + "040DD0F334FC1CA6970100008080312170224008D4F29CDE0EA7D9"; +static const char *simple_submit = "0011000B916407281553F80000AA" + "0AE8329BFD4697D9EC37"; + +static void print_scts(struct sms_scts *scts, const char *prefix) +{ + g_print("%s: (YY-MM-DD) %02d-%02d-%02d\n", prefix, + (int)scts->year, (int)scts->month, (int)scts->day); + + g_print("%s: (HH-MM-SS) %02d:%02d:%02d\n", prefix, + (int)scts->hour, (int)scts->minute, (int)scts->second); + + g_print("%s: Timezone %d hours %d minutes\n", prefix, + (int)scts->timezone / 4, + (int)((abs(scts->timezone) % 4) * 15)); +} + +static void test_simple_deliver() +{ + struct sms sms; + unsigned char *decoded_pdu; + long pdu_len; + gboolean ret; + int data_len; + unsigned char *unpacked; + char *utf8; + + decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0); + + g_assert(decoded_pdu); + g_assert(pdu_len == (long)strlen(simple_deliver) / 2); + + ret = decode_sms(decoded_pdu, pdu_len, FALSE, 30, &sms); + + g_free(decoded_pdu); + + g_assert(ret); + g_assert(sms.type == SMS_TYPE_DELIVER); + + if (g_test_verbose()) { + g_print("SMSC Address number_type: %d, number_plan: %d, %s\n", + (int)sms.sc_addr.number_type, + (int)sms.sc_addr.numbering_plan, sms.sc_addr.address); + + g_print("SMS type: %d\n", (int)sms.type); + + g_print("Originator-Address: %d, %d, %s\n", + (int)sms.deliver.oaddr.number_type, + (int)sms.deliver.oaddr.numbering_plan, + sms.deliver.oaddr.address); + + g_print("PID: %d\n", (int)sms.deliver.pid); + g_print("DCS: %d\n", (int)sms.deliver.dcs); + + print_scts(&sms.deliver.scts, "Timezone"); + } + + g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); + g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); + g_assert(strcmp(sms.sc_addr.address, "31624000000") == 0); + + g_assert(sms.deliver.oaddr.number_type == + SMS_NUMBER_TYPE_INTERNATIONAL); + g_assert(sms.deliver.oaddr.numbering_plan == + SMS_NUMBERING_PLAN_ISDN); + g_assert(strcmp(sms.deliver.oaddr.address, "31641600986") == 0); + + g_assert(sms.deliver.pid == 0); + g_assert(sms.deliver.dcs == 0); + + g_assert(sms.deliver.scts.year == 2); + g_assert(sms.deliver.scts.month == 8); + g_assert(sms.deliver.scts.day == 26); + g_assert(sms.deliver.scts.hour == 19); + g_assert(sms.deliver.scts.minute == 37); + g_assert(sms.deliver.scts.second == 41); + g_assert(sms.deliver.scts.timezone == -4); + + g_assert(sms.deliver.udl == 12); + + data_len = ud_len_in_octets(sms.deliver.udl, sms.deliver.dcs); + + g_assert(data_len == 11); + + unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE, + sms.deliver.udl, NULL, 0xff); + + g_assert(unpacked); + + utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); + + g_free(unpacked); + + g_assert(utf8); + + if (g_test_verbose()) + g_print("Decoded user data is: %s\n", utf8); + + g_assert(strcmp(utf8, "How are you?") == 0); + + g_free(utf8); +} + +static void test_alnum_sender() +{ + struct sms sms; + unsigned char *decoded_pdu; + long pdu_len; + gboolean ret; + int data_len; + unsigned char *unpacked; + char *utf8; + + decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0); + + g_assert(decoded_pdu); + g_assert(pdu_len == (long)strlen(alnum_sender) / 2); + + ret = decode_sms(decoded_pdu, pdu_len, FALSE, 27, &sms); + + g_free(decoded_pdu); + + g_assert(ret); + g_assert(sms.type == SMS_TYPE_DELIVER); + + if (g_test_verbose()) { + g_print("SMSC Address number_type: %d, number_plan: %d, %s\n", + (int)sms.sc_addr.number_type, + (int)sms.sc_addr.numbering_plan, sms.sc_addr.address); + + g_print("SMS type: %d\n", (int)sms.type); + + g_print("Originator-Address: %d, %d, %s\n", + (int)sms.deliver.oaddr.number_type, + (int)sms.deliver.oaddr.numbering_plan, + sms.deliver.oaddr.address); + + g_print("PID: %d\n", (int)sms.deliver.pid); + g_print("DCS: %d\n", (int)sms.deliver.dcs); + + print_scts(&sms.deliver.scts, "Timestamp"); + } + + g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); + g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); + g_assert(strcmp(sms.sc_addr.address, "447785016005") == 0); + + g_assert(sms.deliver.oaddr.number_type == + SMS_NUMBER_TYPE_ALPHANUMERIC); + g_assert(sms.deliver.oaddr.numbering_plan == + SMS_NUMBERING_PLAN_UNKNOWN); + g_assert(strcmp(sms.deliver.oaddr.address, "sipgate") == 0); + + g_assert(sms.deliver.pid == 0); + g_assert(sms.deliver.dcs == 0); + + g_assert(sms.deliver.scts.year == 8); + g_assert(sms.deliver.scts.month == 8); + g_assert(sms.deliver.scts.day == 13); + g_assert(sms.deliver.scts.hour == 12); + g_assert(sms.deliver.scts.minute == 07); + g_assert(sms.deliver.scts.second == 22); + g_assert(sms.deliver.scts.timezone == 4); + + g_assert(sms.deliver.udl == 8); + + data_len = ud_len_in_octets(sms.deliver.udl, sms.deliver.dcs); + + g_assert(data_len == 7); + + unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE, + sms.deliver.udl, NULL, 0xff); + + g_assert(unpacked); + + utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); + + g_free(unpacked); + + g_assert(utf8); + + if (g_test_verbose()) + g_print("Decoded user data is: %s\n", utf8); + + g_assert(strcmp(utf8, "Testmail") == 0); + + g_free(utf8); +} +static void test_deliver_encode() +{ + struct sms sms; + unsigned char *decoded_pdu; + long pdu_len; + gboolean ret; + unsigned char pdu[176]; + int encoded_pdu_len; + int encoded_tpdu_len; + char *encoded_pdu; + + decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0); + + g_assert(decoded_pdu); + g_assert(pdu_len == (long)strlen(simple_deliver) / 2); + + ret = decode_sms(decoded_pdu, pdu_len, FALSE, 30, &sms); + + g_free(decoded_pdu); + + g_assert(ret); + g_assert(sms.type == SMS_TYPE_DELIVER); + + ret = encode_sms(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); + + if (g_test_verbose()) { + int i; + + for (i = 0; i < encoded_pdu_len; i++) + g_print("%02X", pdu[i]); + g_print("\n"); + } + + g_assert(ret); + g_assert(encoded_tpdu_len == 30); + g_assert(encoded_pdu_len == pdu_len); + + encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); + + g_assert(strcmp(simple_deliver, encoded_pdu) == 0); + + g_free(encoded_pdu); + + decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0); + + g_assert(decoded_pdu); + g_assert(pdu_len == (long)strlen(alnum_sender) / 2); + + ret = decode_sms(decoded_pdu, pdu_len, FALSE, 27, &sms); + + g_free(decoded_pdu); + + g_assert(ret); + g_assert(sms.type == SMS_TYPE_DELIVER); + + ret = encode_sms(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); + + if (g_test_verbose()) { + int i; + + for (i = 0; i < encoded_pdu_len; i++) + g_print("%02X", pdu[i]); + g_print("\n"); + } + + g_assert(ret); + g_assert(encoded_tpdu_len == 27); + g_assert(encoded_pdu_len == pdu_len); + + encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); + + g_assert(strcmp(alnum_sender, encoded_pdu) == 0); + + g_free(encoded_pdu); +} + +static void test_simple_submit() +{ + struct sms sms; + unsigned char *decoded_pdu; + long pdu_len; + gboolean ret; + int data_len; + unsigned char *unpacked; + char *utf8; + + decoded_pdu = decode_hex(simple_submit, -1, &pdu_len, 0); + + g_assert(decoded_pdu); + g_assert(pdu_len == (long)strlen(simple_submit) / 2); + + ret = decode_sms(decoded_pdu, pdu_len, TRUE, 23, &sms); + + g_free(decoded_pdu); + + g_assert(ret); + g_assert(sms.type == SMS_TYPE_SUBMIT); + + if (g_test_verbose()) { + if (sms.sc_addr.address[0] == '\0') + g_print("SMSC Address absent, default will be used\n"); + else + g_print("SMSC Address number_type: %d," + " number_plan: %d, %s\n", + (int)sms.sc_addr.number_type, + (int)sms.sc_addr.numbering_plan, + sms.sc_addr.address); + + g_print("SMS type: %d\n", (int)sms.type); + + g_print("Message Reference: %u\n", (int)sms.submit.mr); + + g_print("Destination-Address: %d, %d, %s\n", + (int)sms.submit.daddr.number_type, + (int)sms.submit.daddr.numbering_plan, + sms.submit.daddr.address); + + g_print("PID: %d\n", (int)sms.submit.pid); + g_print("DCS: %d\n", (int)sms.submit.dcs); + + g_print("Validity Period Format: %d\n", (int)sms.submit.vpf); + + switch (sms.submit.vpf) { + case SMS_VALIDITY_PERIOD_FORMAT_ABSENT: + g_print("Validity-Period: Absent\n"); + break; + case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE: + g_print("Validity-Period: %d\n", + (int)sms.submit.vp.relative); + break; + case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: + print_scts(&sms.submit.vp.absolute, "Validity-Period:"); + break; + case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED: + g_print("Validity-Period: Enhanced"); + break; + } + } + + g_assert(strlen(sms.sc_addr.address) == 0); + + g_assert(sms.submit.mr == 0); + + g_assert(sms.submit.daddr.number_type == + SMS_NUMBER_TYPE_INTERNATIONAL); + g_assert(sms.submit.daddr.numbering_plan == + SMS_NUMBERING_PLAN_ISDN); + g_assert(strcmp(sms.submit.daddr.address, "46708251358") == 0); + + g_assert(sms.submit.pid == 0); + g_assert(sms.submit.dcs == 0); + + g_assert(sms.submit.vpf == SMS_VALIDITY_PERIOD_FORMAT_RELATIVE); + g_assert(sms.submit.vp.relative == 0xAA); + + g_assert(sms.submit.udl == 10); + + data_len = ud_len_in_octets(sms.submit.udl, sms.submit.dcs); + + g_assert(data_len == 9); + + unpacked = unpack_7bit(sms.submit.ud, data_len, 0, FALSE, + sms.submit.udl, NULL, 0xff); + + g_assert(unpacked); + + utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); + + g_free(unpacked); + + g_assert(utf8); + + if (g_test_verbose()) + g_print("Decoded user data is: %s\n", utf8); + + g_assert(strcmp(utf8, "hellohello") == 0); + + g_free(utf8); +} + +static void test_submit_encode() +{ + +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/testsms/Test Simple Deliver", test_simple_deliver); + g_test_add_func("/testsms/Test Alnum Deliver", test_alnum_sender); + g_test_add_func("/testsms/Test Deliver Encode", test_deliver_encode); + g_test_add_func("/testsms/Test Simple Submit", test_simple_submit); + g_test_add_func("/testsms/Test Submit Encode", test_submit_encode); + + return g_test_run(); +} diff --git a/unit/test-util.c b/unit/test-util.c new file mode 100644 index 00000000..11cd6b3a --- /dev/null +++ b/unit/test-util.c @@ -0,0 +1,629 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2009 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "util.h" + +const unsigned char invalid_gsm_extended[] = { + 0x1b, 0x15 +}; + +const unsigned char invalid_gsm_extended_len[] = { + 0x1b, 0x28, 0x1b +}; + +unsigned short gsm_to_unicode_map[] = +{ +0x00, 0x0040, +0x01, 0x00A3, +0x02, 0x0024, +0x03, 0x00A5, +0x04, 0x00E8, +0x05, 0x00E9, +0x06, 0x00F9, +0x07, 0x00EC, +0x08, 0x00F2, +0x09, 0x00E7, +0x0A, 0x000A, +0x0B, 0x00D8, +0x0C, 0x00F8, +0x0D, 0x000D, +0x0E, 0x00C5, +0x0F, 0x00E5, +0x10, 0x0394, +0x11, 0x005F, +0x12, 0x03A6, +0x13, 0x0393, +0x14, 0x039B, +0x15, 0x03A9, +0x16, 0x03A0, +0x17, 0x03A8, +0x18, 0x03A3, +0x19, 0x0398, +0x1A, 0x039E, +/*0x1B, 0x00A0,*/ +0x1B0A, 0x000C, +0x1B14, 0x005E, +0x1B28, 0x007B, +0x1B29, 0x007D, +0x1B2F, 0x005C, +0x1B3C, 0x005B, +0x1B3D, 0x007E, +0x1B3E, 0x005D, +0x1B40, 0x007C, +0x1B65, 0x20AC, +0x1C, 0x00C6, +0x1D, 0x00E6, +0x1E, 0x00DF, +0x1F, 0x00C9, +0x20, 0x0020, +0x21, 0x0021, +0x22, 0x0022, +0x23, 0x0023, +0x24, 0x00A4, +0x25, 0x0025, +0x26, 0x0026, +0x27, 0x0027, +0x28, 0x0028, +0x29, 0x0029, +0x2A, 0x002A, +0x2B, 0x002B, +0x2C, 0x002C, +0x2D, 0x002D, +0x2E, 0x002E, +0x2F, 0x002F, +0x30, 0x0030, +0x31, 0x0031, +0x32, 0x0032, +0x33, 0x0033, +0x34, 0x0034, +0x35, 0x0035, +0x36, 0x0036, +0x37, 0x0037, +0x38, 0x0038, +0x39, 0x0039, +0x3A, 0x003A, +0x3B, 0x003B, +0x3C, 0x003C, +0x3D, 0x003D, +0x3E, 0x003E, +0x3F, 0x003F, +0x40, 0x00A1, +0x41, 0x0041, +0x42, 0x0042, +0x43, 0x0043, +0x44, 0x0044, +0x45, 0x0045, +0x46, 0x0046, +0x47, 0x0047, +0x48, 0x0048, +0x49, 0x0049, +0x4A, 0x004A, +0x4B, 0x004B, +0x4C, 0x004C, +0x4D, 0x004D, +0x4E, 0x004E, +0x4F, 0x004F, +0x50, 0x0050, +0x51, 0x0051, +0x52, 0x0052, +0x53, 0x0053, +0x54, 0x0054, +0x55, 0x0055, +0x56, 0x0056, +0x57, 0x0057, +0x58, 0x0058, +0x59, 0x0059, +0x5A, 0x005A, +0x5B, 0x00C4, +0x5C, 0x00D6, +0x5D, 0x00D1, +0x5E, 0x00DC, +0x5F, 0x00A7, +0x60, 0x00BF, +0x61, 0x0061, +0x62, 0x0062, +0x63, 0x0063, +0x64, 0x0064, +0x65, 0x0065, +0x66, 0x0066, +0x67, 0x0067, +0x68, 0x0068, +0x69, 0x0069, +0x6A, 0x006A, +0x6B, 0x006B, +0x6C, 0x006C, +0x6D, 0x006D, +0x6E, 0x006E, +0x6F, 0x006F, +0x70, 0x0070, +0x71, 0x0071, +0x72, 0x0072, +0x73, 0x0073, +0x74, 0x0074, +0x75, 0x0075, +0x76, 0x0076, +0x77, 0x0077, +0x78, 0x0078, +0x79, 0x0079, +0x7A, 0x007A, +0x7B, 0x00E4, +0x7C, 0x00F6, +0x7D, 0x00F1, +0x7E, 0x00FC, +0x7F, 0x00E0, +}; + +#define UTF8_LENGTH(c) \ + ((c) < 0x80 ? 1 : \ + ((c) < 0x800 ? 2 : 3)) + +static void test_invalid() +{ + long nwritten; + long nread; + char *res; + + res = convert_gsm_to_utf8(invalid_gsm_extended, 0, &nread, &nwritten, + 0); + g_assert(res == NULL); + g_assert(nread == 0); + + res = convert_gsm_to_utf8(invalid_gsm_extended, + sizeof(invalid_gsm_extended), + &nread, &nwritten, 0); + g_assert(res == NULL); + g_assert(nread == 1); + + res = convert_gsm_to_utf8(invalid_gsm_extended_len, + sizeof(invalid_gsm_extended_len), + &nread, &nwritten, 0); + g_assert(res == NULL); + g_assert(nread == 3); +} + +static void test_valid() +{ + long nwritten; + long nread; + char *res; + int i; + long size; + gunichar *verify; + unsigned char *back; + + unsigned char buf[2]; + + static int map_size = + sizeof(gsm_to_unicode_map) / sizeof(unsigned short) / 2; + + for (i = 0; i < map_size; i++) { + unsigned short c = gsm_to_unicode_map[i*2]; + + if (c & 0x1b00) { + buf[0] = 0x1b; + buf[1] = c & 0x7f; + size = 2; + } else { + size = 1; + buf[0] = c & 0x7f; + } + + res = convert_gsm_to_utf8(buf, size, &nread, &nwritten, 0); + g_assert(res); + + if (g_test_verbose()) + g_print("size: %ld, nread:%ld, nwritten:%ld, %s\n", + size, nread, nwritten, res); + + g_assert(nread == size); + + verify = g_utf8_to_ucs4(res, -1, NULL, NULL, NULL); + + g_assert(verify[0] == gsm_to_unicode_map[i*2+1]); + g_assert(verify[1] == 0); + + g_assert(nwritten == UTF8_LENGTH(verify[0])); + + back = convert_utf8_to_gsm(res, -1, &nread, &nwritten, 0); + + g_assert(back); + + g_assert(nwritten == size); + if (c & 0x1b00) { + g_assert(back[0] == 0x1b); + g_assert(back[1] == (c & 0x7f)); + } else { + g_assert(back[0] == (c & 0x7f)); + } + + g_free(back); + g_free(verify); + g_free(res); + } +} + +static const char hex_packed[] = "493A283D0795C3F33C88FE06C9CB6132885EC6D34" + "1EDF27C1E3E97E7207B3A0C0A5241E377BB1D" + "7693E72E"; +static const char expected[] = "It is easy to read text messages via AT commands."; +static int reported_text_size = 49; + +static void test_decode_encode() +{ + const char *sms = hex_packed; + unsigned char *decoded, *packed; + char *utf8, *hex_packed; + unsigned char *gsm, *gsm_encoded; + long hex_decoded_size; + long unpacked_size, packed_size; + long gsm_encoded_size; + long i; + + if (g_test_verbose()) + g_print("Size of the orig string: %u\n", + (unsigned int)strlen(sms)); + + decoded = decode_hex(sms, -1, &hex_decoded_size, 0); + + g_assert(decoded != NULL); + + if (g_test_verbose()) + g_print("Decode to %ld bytes\n", hex_decoded_size); + + if (g_test_verbose()) { + g_print("%s\n", sms); + + for (i = 0; i < hex_decoded_size; i++) + g_print("%02X", decoded[i]); + g_print("\n"); + } + + gsm = unpack_7bit(decoded, hex_decoded_size, 0, FALSE, + reported_text_size, &unpacked_size, 0xff); + + g_assert(gsm != NULL); + + if (g_test_verbose()) + g_print("String unpacked to %ld bytes\n", unpacked_size); + + utf8 = convert_gsm_to_utf8(gsm, -1, NULL, NULL, 0xff); + + g_assert(utf8 != NULL); + + if (g_test_verbose()) + g_print("String is: -->%s<--\n", utf8); + + g_assert(strcmp(utf8, expected) == 0); + + gsm_encoded = convert_utf8_to_gsm(utf8, -1, NULL, + &gsm_encoded_size, 0xff); + + g_assert(gsm_encoded != NULL); + + if (g_test_verbose()) + g_print("Converted back to GSM string of %ld bytes\n", + gsm_encoded_size); + + g_assert(gsm_encoded[gsm_encoded_size] == 0xff); + g_assert(gsm_encoded_size == unpacked_size); + g_assert(memcmp(gsm_encoded, gsm, gsm_encoded_size) == 0); + + g_free(utf8); + g_free(gsm); + + packed = pack_7bit(gsm_encoded, -1, 0, FALSE, &packed_size, 0xff); + + g_assert(gsm_encoded != NULL); + + if (g_test_verbose()) + g_print("Packed GSM to size of %ld bytes\n", packed_size); + + if (g_test_verbose()) { + for (i = 0; i < packed_size; i++) + g_print("%02X", packed[i]); + g_print("\n"); + } + + g_assert(packed_size == hex_decoded_size); + g_assert(memcmp(packed, decoded, packed_size) == 0); + + g_free(decoded); + + hex_packed = encode_hex(packed, packed_size, 0); + + g_assert(hex_packed != NULL); + + g_free(packed); + + if (g_test_verbose()) + g_print("Hex encoded packed to size %ld bytes\n", + (long)strlen(hex_packed)); + + g_assert(strlen(hex_packed) == strlen(sms)); + g_assert(strcmp(hex_packed, sms) == 0); + + g_free(hex_packed); +} + +static void test_pack_size() +{ + unsigned char c1[] = { 'a' }; + unsigned char c2[] = { 'a', 'b' }; + unsigned char c3[] = { 'a', 'b', 'c' }; + unsigned char c4[] = { 'a', 'b', 'c', 'd' }; + unsigned char c5[] = { 'a', 'b', 'c', 'd', 'e' }; + unsigned char c6[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; + unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + + unsigned char *packed; + long size; + + packed = pack_7bit(c1, 1, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 1); + g_free(packed); + + packed = pack_7bit(c2, 2, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 2); + g_free(packed); + + packed = pack_7bit(c3, 3, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 3); + g_free(packed); + + packed = pack_7bit(c4, 4, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 4); + g_free(packed); + + packed = pack_7bit(c5, 5, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 5); + g_free(packed); + + packed = pack_7bit(c6, 6, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 6); + g_free(packed); + + packed = pack_7bit(c7, 7, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 7); + g_assert((packed[6] & 0xfe) == 0); + g_free(packed); + + packed = pack_7bit(c7, 7, 0, TRUE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 7); + g_assert(((packed[6] & 0xfe) >> 1) == '\r'); + g_free(packed); + + packed = pack_7bit(c8, 8, 0, FALSE, &size, 0); + g_assert(packed != NULL); + g_assert(size == 7); + g_free(packed); +} + +static void test_cr_handling() +{ + unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + unsigned char c7_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r' }; + unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r' }; + unsigned char c8_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r', '\r' }; + + unsigned char *packed; + unsigned char *unpacked; + long packed_size; + long unpacked_size; + + packed = pack_7bit(c8, 8, 0, TRUE, &packed_size, 0); + g_assert(packed != NULL); + g_assert(packed_size == 8); + g_assert(((packed[6] & 0xfe) >> 1) == '\r'); + g_assert((packed[7] & 0x7f) == '\r'); + + unpacked = unpack_7bit(packed, 8, 0, TRUE, -1, &unpacked_size, 0); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 9); + g_assert(memcmp(c8_expected, unpacked, 9) == 0); + + g_free(unpacked); + g_free(packed); + + packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0); + g_assert(packed != NULL); + g_assert(packed_size == 7); + g_assert(((packed[6] & 0xfe) >> 1) == '\r'); + + unpacked = unpack_7bit(packed, 7, 0, TRUE, -1, &unpacked_size, 0); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 7); + g_assert(memcmp(c7, unpacked, 7) == 0); + + g_free(unpacked); + g_free(packed); + + /* As above, but now unpack using SMS style, we should now have cr at + * the end of the stream + */ + packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0); + unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 8); + g_assert(memcmp(c7_expected, unpacked, 8) == 0); + + g_free(unpacked); + g_free(packed); +} + +static void test_sms_handling() +{ + unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + + unsigned char *packed; + unsigned char *unpacked; + long packed_size; + long unpacked_size; + + packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0); + g_assert(packed != NULL); + g_assert(packed_size == 7); + + unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0xff); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 8); + g_assert(unpacked[7] == 0); + g_assert(unpacked[8] == 0xff); + + g_free(unpacked); + g_free(packed); + + packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0); + g_assert(packed != NULL); + g_assert(packed_size == 7); + + unpacked = unpack_7bit(packed, 7, 0, FALSE, 7, &unpacked_size, 0xff); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 7); + g_assert(unpacked[7] == 0xff); + + g_free(unpacked); + g_free(packed); +} + +static void test_offset_handling() +{ + unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; + unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + unsigned char c9[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' }; + unsigned char *packed; + unsigned char *unpacked; + long packed_size; + long unpacked_size; + + /* Pack at offset = 2 bytes, e.g. starting with 21st bit */ + packed = pack_7bit(c7, 6, 2, FALSE, &packed_size, 0); + + if (g_test_verbose()) + g_print("Packed to size: %ld\n", packed_size); + + g_assert(packed != NULL); + g_assert(packed_size == 6); + + unpacked = unpack_7bit(packed, 6, 2, FALSE, 6, &unpacked_size, 0xff); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 6); + g_assert(unpacked[6] == 0xff); + g_assert(unpacked[0] == 'a'); + g_assert(unpacked[5] == 'f'); + + g_free(unpacked); + g_free(packed); + + /* Pack at offset = 6 bytes, we should be able to fit one character + * into the first byte, and the other 7 characters into the following + * 7 bytes. The 7 MSB bits of the last byte should be 0 since + * we're not using CBS packing + */ + packed = pack_7bit(c8, 8, 6, FALSE, &packed_size, 0); + + if (g_test_verbose()) + g_print("Packed to size: %ld\n", packed_size); + + g_assert(packed != NULL); + g_assert(packed_size == 8); + + unpacked = unpack_7bit(packed, 8, 6, FALSE, 8, &unpacked_size, 0xff); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 8); + g_assert(unpacked[8] == 0xff); + g_assert(unpacked[0] == 'a'); + g_assert(unpacked[7] == 'h'); + + g_free(unpacked); + g_free(packed); + + /* Same as above, but instead pack in 9 characters */ + packed = pack_7bit(c9, 9, 6, FALSE, &packed_size, 0); + + if (g_test_verbose()) + g_print("Packed to size: %ld\n", packed_size); + + g_assert(packed != NULL); + g_assert(packed_size == 8); + + unpacked = unpack_7bit(packed, 8, 6, FALSE, 9, &unpacked_size, 0xff); + if (g_test_verbose()) + g_print("Unpacked to size: %ld\n", unpacked_size); + + g_assert(unpacked != NULL); + g_assert(unpacked_size == 9); + g_assert(unpacked[9] == 0xff); + g_assert(unpacked[0] == 'a'); + g_assert(unpacked[8] == 'i'); + + g_free(unpacked); + g_free(packed); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/testutil/Invalid Conversions", test_invalid); + g_test_add_func("/testutil/Valid Conversions", test_valid); + g_test_add_func("/testutil/Decode Encode", test_decode_encode); + g_test_add_func("/testutil/Pack Size", test_pack_size); + g_test_add_func("/testutil/CBS CR Handling", test_cr_handling); + g_test_add_func("/testutil/SMS Handling", test_sms_handling); + g_test_add_func("/testutil/Offset Handling", test_offset_handling); + + return g_test_run(); +}