Index: .github/workflows/ci.yml
===================================================================
--- .github/workflows/ci.yml	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .github/workflows/ci.yml	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,221 @@
+name: CI
+
+on:
+  - pull_request
+  - push
+
+jobs:
+  jabber:
+    runs-on: ubuntu-20.04
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Dependencies
+      run: |
+        sudo apt-get update -y
+        sudo apt-get install -y --no-install-recommends clang git make pkg-config libgnutls28-dev libgcrypt20-dev libglib2.0-dev prosody gcovr
+        sudo prosodyctl stop
+
+    - name: build bitlbee
+      run: |
+        ./configure --asan=1 --twitter=0
+        cat ./.tests/gcovflags >> Makefile.settings
+        make
+        sudo make install
+        sudo make install-etc
+
+        touch ./debuglog
+
+        sudo mkdir /var/lib/bitlbee
+        sudo chmod -R 777 /var/lib/bitlbee
+    
+    - name: talk to bitlbee
+      run: |
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/talk_to_bitlbee.py
+
+        ./.tests/cleanup_bitlbee.sh
+    
+    - name: ask for help
+      run: |
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/help.py
+
+        ./.tests/cleanup_bitlbee.sh
+
+    - name: (un)register
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/register.py
+        python3 ./.tests/identify.py
+        python3 ./.tests/unregister.py
+        python3 ./.tests/identify_nonexist.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber connect
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_connect.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber delete account
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_delete_acc.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber add buddy
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_add_buddy.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber remove buddy (leakage ok)
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_remove_buddy.py
+
+        ./.tests/cleanup_bitlbee_leakage_ok.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber send message
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_send_msg.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber rename buddy
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_rename_buddy.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber set status
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_status.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber go offline (leakage ok)
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_offline.py
+
+        ./.tests/cleanup_bitlbee_leakage_ok.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber change default target
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_default_target.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber block buddy
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_block_buddy.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: code coverage
+      if: ${{ always() }}
+      run: |
+        gcovr -r .
+
+  known_bugs:
+    runs-on: ubuntu-20.04
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Dependencies
+      run: |
+        sudo apt-get update -y
+        sudo apt-get install -y --no-install-recommends clang git make pkg-config libgnutls28-dev libgcrypt20-dev libglib2.0-dev prosody gcovr
+        sudo prosodyctl stop
+
+    - name: build bitlbee
+      run: |
+        ./configure --asan=1 --twitter=0
+        cat ./.tests/gcovflags >> Makefile.settings
+        make
+        sudo make install
+        sudo make install-etc
+
+        touch ./debuglog
+
+        sudo mkdir /var/lib/bitlbee
+        sudo chmod -R 777 /var/lib/bitlbee
+
+    - name: jabber remove buddy (leakage ok)
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_remove_buddy.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
+
+    - name: jabber go offline
+      if: ${{ always() }}
+      run: |
+        ./.tests/setup_prosody.sh
+        ./.tests/setup_bitlbee.sh
+
+        python3 ./.tests/jabber_offline.py
+
+        ./.tests/cleanup_bitlbee.sh
+        ./.tests/cleanup_prosody.sh
Index: .tests/btlib.py
===================================================================
--- .tests/btlib.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/btlib.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,304 @@
+import socket
+import sys
+import time
+import select
+
+FUN = [
+"Did I ask you something?",
+"Oh yeah, that's right.",
+"Alright, alright. Now go back to work.",
+"Buuuuuuuuuuuuuuuurp... Excuse me!",
+"Yes?",
+"No?",
+]
+
+SEPARATOR = "="*60
+
+ALWAYSSHOWLOG = False
+
+class IrcClient:
+    def __init__(self, nick, pwd):
+        self.nick = nick
+        self.pwd = pwd
+        self.log = ''
+        self.tmplog = ''
+        self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+    def send_raw(self, msg, loud = False, log = True):
+        self.receive()
+        if loud:
+            print('FROM '+ self.nick + '|| ' + msg)
+        if log:
+            self.log += msg+'\r\n'
+            self.tmplog += msg+'\r\n'
+        self.sck.send((msg+'\r\n').encode())
+
+    def send_priv_msg(self, recip, msg, loud = False):
+        self.send_raw('PRIVMSG '+recip+' :'+msg, loud)
+
+    def connect(self):
+        connected = False
+        for x in range(5):
+            try:
+                self.sck.connect(('127.0.0.1', 6667))
+                connected = True
+                break
+            except:
+                time.sleep(1)
+
+        if not connected:
+            return False
+
+        self.send_raw('USER ' + (self.nick + " ")*3)
+        self.send_raw('NICK ' + self.nick)
+        self.send_raw('JOIN &bitlbee')
+        return True
+
+    def jabber_login(self):
+        self.send_priv_msg("&bitlbee", "account add jabber "+self.nick+"@localhost "+self.pwd)
+        time.sleep(0.3)
+        self.send_priv_msg("&bitlbee", "account on")
+        time.sleep(1)
+        self.receive()
+        return (self.log.find('Logged in') != -1)
+
+    def receive(self, wait = 1):
+        text = ''
+        while True:
+            readable, _, _ = select.select([self.sck], [], [], 5)
+            if self.sck in readable:
+                text += self.sck.recv(2040).decode()
+                for line in text.split('\n'):
+                    if line.find('PING') != -1:
+                        self.send_raw('PONG ' + line.split()[1])
+            else:
+                time.sleep(wait)
+                if self.sck not in readable:
+                    break
+        self.log += text
+        self.tmplog += text
+        return text
+
+    def add_jabber_buddy(self, nick):
+        self.send_priv_msg("&bitlbee", "add 0 " + nick+"@localhost")
+    
+    def block_jabber_buddy(self, nick):
+        self.send_priv_msg("&bitlbee", "block " + nick)
+
+    def unblock_jabber_buddy(self, nick):
+        self.send_priv_msg("&bitlbee", "allow " + nick)
+
+    def rename_jabber_buddy(self, oldnick, newnick):
+        self.send_priv_msg("&bitlbee", "rename " + oldnick + " " + newnick)
+        
+def msg_comes_thru(sender, receiver, message):
+    sender.send_priv_msg(receiver.nick, message)
+    received = receiver.receive().find(message) != -1
+    return received
+
+def perform_test(test_function):
+    clis = []
+    clis += [IrcClient('test1', 'asd')]
+    clis += [IrcClient('test2', 'asd')]
+
+    fail = not test_function(clis)
+
+    if ALWAYSSHOWLOG or fail:
+        print(test_function.__name__ + ' logs:')
+        for cli in clis:
+            cli.receive()
+            print(SEPARATOR)
+            print("Test Log "+ cli.nick+":")
+            print(cli.tmplog)
+        print(SEPARATOR)
+    else:
+        print(test_function.__name__ + ' succeeded')
+    
+    if fail:
+        sys.exit(1)
+
+def connect_test(clis):
+    ret = True
+    for cli in clis:
+        ret = ret & cli.connect()
+    return ret
+
+def yes_test(clis):
+    ret = False
+    clis[0].send_priv_msg("&bitlbee", "yes")
+    clis[0].receive()
+    for x, fun in enumerate(FUN):
+        if (clis[0].log.find(fun) != -1):
+            ret = True
+            if x:
+                print("The RNG gods smile upon us")
+            break
+    return ret
+
+def jabber_login_test(clis):
+    ret = True
+    for cli in clis:
+        ret = ret & cli.jabber_login()
+    return ret
+
+def jabber_delete_account_test(clis):
+    ret = True
+    clis[1].receive()
+    clis[1].send_priv_msg("&bitlbee", "account list")
+    ret = ret & (clis[1].receive().find(clis[1].nick+'@localhost') != -1)
+
+    clis[1].send_priv_msg("&bitlbee", "account on")
+    clis[1].send_priv_msg("&bitlbee", "account "+clis[1].nick+" del")
+    clis[1].receive()
+    clis[1].send_priv_msg("&bitlbee", "account list")
+    ret = ret & (clis[1].receive().find(clis[1].nick+'@localhost') != -1)
+
+    clis[1].send_priv_msg("&bitlbee", "account off")
+    clis[1].send_priv_msg("&bitlbee", "account "+clis[1].nick+" del")
+    clis[1].receive()
+    clis[1].send_priv_msg("&bitlbee", "account list")
+    ret = ret & (clis[1].receive().find(clis[1].nick+'@localhost') == -1)
+    return ret
+
+def register_test(clis):
+    clis[1].send_priv_msg("&bitlbee", "register "+clis[1].pwd*2)
+    ret = (clis[1].receive(wait = 10).find('Account successfully created') != -1)
+    return ret
+    
+def unregister_test(clis):
+    clis[1].send_priv_msg("&bitlbee", "drop "+clis[1].pwd*2)
+    ret = (clis[1].receive().find('removed') != -1)
+    clis[1].send_priv_msg("&bitlbee", "drop "+clis[1].pwd*2)
+    ret = ret & (clis[1].receive().find('That account does not exist') != -1)
+    return ret
+
+def identify_test(clis):
+    ret = True
+    clis[1].send_priv_msg("&bitlbee", "identify "+clis[1].pwd)
+    ret = ret & (clis[1].receive().find('Incorrect password') != -1)
+
+    clis[1].send_priv_msg("&bitlbee", "identify "+clis[1].pwd*2)
+    ret = ret & (clis[1].receive().find('Password accepted') != -1)
+    return ret
+
+def identify_nonexist_test(clis):
+    clis[1].send_priv_msg("&bitlbee", "identify "+clis[1].pwd)
+    return (clis[1].receive().find('The nick is (probably) not registered') != -1)
+
+def add_buddy_test(clis):
+    clis[0].add_jabber_buddy(clis[1].nick)
+    clis[1].send_priv_msg("&bitlbee", "yes")
+
+    clis[1].add_jabber_buddy(clis[0].nick)
+    clis[0].send_priv_msg("&bitlbee", "yes")
+
+    clis[0].send_priv_msg("&bitlbee", "blist")
+    junk = clis[0].receive()
+    ret = junk.find(clis[1].nick) != -1
+    ret = ret & (junk.find("1 available") != -1)
+    return ret
+
+def remove_buddy_test(clis):
+    ret = add_buddy_test(clis)
+
+    clis[0].send_priv_msg("&bitlbee", "remove " +clis[1].nick)
+    clis[0].send_priv_msg("&bitlbee", "blist all")
+    time.sleep(0.5)
+    ret = ret & (clis[0].receive().find(clis[1].nick) == -1)
+
+    clis[0].add_jabber_buddy(clis[1].nick)
+    clis[1].send_priv_msg("&bitlbee", "yes")
+    clis[0].send_priv_msg("&bitlbee", "blist all")
+    junk = clis[0].receive(wait=5)
+    #ret = ret & (junk.find("1 available") != -1)
+    ret = ret & (junk.find(clis[1].nick) != -1)
+    return ret
+
+def message_test(clis):
+    ret = msg_comes_thru(clis[0], clis[1], 'ohai <3')
+    ret = ret & msg_comes_thru(clis[1], clis[0], 'uwu *pounces*')
+    return ret
+
+def block_test(clis):
+    clis[0].block_jabber_buddy(clis[1].nick)
+    ret = not msg_comes_thru(clis[1], clis[0], 'm-meow?')
+    clis[0].unblock_jabber_buddy(clis[1].nick)
+    ret = ret & msg_comes_thru(clis[1], clis[0], '*purrs*')
+    return ret
+
+def rename_test(clis):
+    newname = "xXx_pup_LINKENPARK4EVA"
+    message = "rawr meanmz i luv<3 u in dinosaur"
+
+    clis[0].rename_jabber_buddy(clis[1].nick, newname)
+    clis[0].send_priv_msg(newname, message)
+    ret = clis[1].receive().find(message) != -1
+
+    clis[0].rename_jabber_buddy("-del", newname)
+    ret = ret & msg_comes_thru(clis[0], clis[1], "rawr")
+    return ret
+
+def status_test(clis):
+    status = "get out of my room mom"
+    clis[1].send_priv_msg("&bitlbee", "set status '"+status+"'")
+    clis[0].send_priv_msg("&bitlbee", "info "+clis[1].nick)
+    ret = (clis[0].receive().find("jabber - Status message: "+status) != -1)
+
+    clis[1].send_priv_msg("&bitlbee", "set")
+    ret = ret & (clis[1].receive().find(status) != -1)
+
+    clis[1].send_priv_msg("&bitlbee", "set -del status")
+    clis[0].send_priv_msg("&bitlbee", "info "+clis[1].nick)
+    ret = ret & (clis[0].receive().find("jabber - Status message: (none)") != -1)
+    return ret
+
+def offline_test(clis):
+    clis[0].send_priv_msg("&bitlbee", "account off")
+
+    junk = clis[0].receive()
+    ret = (junk.find(clis[1].nick) != -1)
+    ret = ret & (junk.find("QUIT") != -1)
+
+    junk = clis[1].receive()
+    ret = ret & (junk.find(clis[0].nick) != -1)
+    ret = ret & (junk.find("QUIT") != -1)
+
+    clis[0].send_priv_msg(clis[1].nick, "i'm not ur mom")
+    ret = ret & (clis[0].receive().find("No such nick/channel") != -1)
+
+    clis[0].send_priv_msg("&bitlbee", "account on")
+
+    junk = clis[0].receive()
+    ret = ret & (junk.find(clis[1].nick) != -1)
+    ret = ret & (junk.find("JOIN") != -1)
+
+    junk = clis[1].receive()
+    ret = ret & (junk.find(clis[0].nick) != -1)
+    ret = ret & (junk.find("JOIN") != -1)
+
+    return ret
+
+def default_target_test(clis):
+    clis[0].send_priv_msg("&bitlbee", "set default_target last")
+    clis[0].send_priv_msg("&bitlbee", "test2: ur mah default now")
+    
+    ret = (clis[1].receive().find("ur mah default now") != -1)
+
+    clis[0].send_priv_msg("&bitlbee", "mrow")
+    ret = ret & (clis[1].receive().find("mrow") != -1)
+
+    clis[0].send_priv_msg("root", "set default_target root")
+    junk = clis[0].receive()
+    ret = ret & (junk.find("default_target") != -1)
+    ret = ret & (junk.find("root") != -1)
+
+    clis[0].send_priv_msg("&bitlbee", "yes")
+    ret = ret & (clis[1].receive().find("yes") == -1)
+    return ret 
+
+def help_test(clis):
+    clis[0].send_priv_msg("&bitlbee", "help")
+    ret = (clis[0].receive().find("identify_methods") != -1)
+    clis[0].send_priv_msg("&bitlbee", "help commands")
+    ret = ret & (clis[0].receive().find("qlist") != -1)
+    return ret
Index: .tests/cleanup_bitlbee.sh
===================================================================
--- .tests/cleanup_bitlbee.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/cleanup_bitlbee.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+killall bitlbee || true
+rm -r /var/lib/bitlbee/* || true
+sudo make uninstall-etc
+
+printf 'Bitlbee output:\n'
+less ./debuglog
+
+if cat ./debuglog | grep -i -q error
+then
+    rm ./debuglog
+    exit 1
+else
+    rm ./debuglog
+fi
Index: .tests/cleanup_bitlbee_leakage_ok.sh
===================================================================
--- .tests/cleanup_bitlbee_leakage_ok.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/cleanup_bitlbee_leakage_ok.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+killall bitlbee || true
+rm -r /var/lib/bitlbee/* || true
+sudo make uninstall-etc
+
+printf 'Bitlbee output:\n'
+less ./debuglog
Index: .tests/cleanup_prosody.sh
===================================================================
--- .tests/cleanup_prosody.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/cleanup_prosody.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+sudo prosodyctl deluser test1@localhost
+sudo prosodyctl deluser test2@localhost
+sudo prosodyctl stop
Index: .tests/gcovflags
===================================================================
--- .tests/gcovflags	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/gcovflags	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,4 @@
+CFLAGS+=-fprofile-arcs -ftest-coverage -O0
+CFLAGS_BITLBEE+=-fprofile-arcs -ftest-coverage -O0
+LDFLAGS+=-lgcov --coverage
+LDFLAGS_BITLBEE+=-lgcov --coverage
Index: .tests/help.py
===================================================================
--- .tests/help.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/help.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def help(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.help_test(clis)
+    return ret
+
+btlib.perform_test(help)
Index: .tests/identify.py
===================================================================
--- .tests/identify.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/identify.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def identify(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.identify_test(clis)
+    return ret
+
+btlib.perform_test(identify)
Index: .tests/identify_nonexist.py
===================================================================
--- .tests/identify_nonexist.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/identify_nonexist.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def identify_nonexist(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.identify_nonexist_test(clis)
+    return ret
+
+btlib.perform_test(identify_nonexist)
Index: .tests/jabber_add_buddy.py
===================================================================
--- .tests/jabber_add_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_add_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+import btlib
+
+def add_buddy(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    return ret
+
+btlib.perform_test(add_buddy)
Index: .tests/jabber_block_buddy.py
===================================================================
--- .tests/jabber_block_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_block_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def block_buddy(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.block_test(clis)
+    return ret
+
+btlib.perform_test(block_buddy)
Index: .tests/jabber_connect.py
===================================================================
--- .tests/jabber_connect.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_connect.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def jabber_connect(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    return ret
+
+btlib.perform_test(jabber_connect)
Index: .tests/jabber_default_target.py
===================================================================
--- .tests/jabber_default_target.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_default_target.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def default_target(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.default_target_test(clis)
+    return ret
+
+btlib.perform_test(default_target)
Index: .tests/jabber_delete_acc.py
===================================================================
--- .tests/jabber_delete_acc.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_delete_acc.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+import btlib
+
+def jabber_delete_acc(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.jabber_delete_account_test(clis)
+    return ret
+
+btlib.perform_test(jabber_delete_acc)
Index: .tests/jabber_offline.py
===================================================================
--- .tests/jabber_offline.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_offline.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def offline(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.offline_test(clis)
+    return ret
+
+btlib.perform_test(offline)
Index: .tests/jabber_remove_buddy.py
===================================================================
--- .tests/jabber_remove_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_remove_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+import btlib
+
+def remove_buddy(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.remove_buddy_test(clis)
+    return ret
+
+btlib.perform_test(remove_buddy)
Index: .tests/jabber_rename_buddy.py
===================================================================
--- .tests/jabber_rename_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_rename_buddy.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def rename_buddy(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.rename_test(clis)
+    return ret
+
+btlib.perform_test(rename_buddy)
Index: .tests/jabber_send_msg.py
===================================================================
--- .tests/jabber_send_msg.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_send_msg.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def send_msg(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.message_test(clis)
+    return ret
+
+btlib.perform_test(send_msg)
Index: .tests/jabber_status.py
===================================================================
--- .tests/jabber_status.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/jabber_status.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,11 @@
+import btlib
+
+def status(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.add_buddy_test(clis)
+    ret = ret & btlib.status_test(clis)
+    return ret
+
+btlib.perform_test(status)
Index: .tests/register.py
===================================================================
--- .tests/register.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/register.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+import btlib
+
+def register(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.jabber_login_test(clis)
+    ret = ret & btlib.register_test(clis)
+    return ret
+
+btlib.perform_test(register)
Index: .tests/setup_bitlbee.sh
===================================================================
--- .tests/setup_bitlbee.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/setup_bitlbee.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+
+killall bitlbee >/dev/null 2>/dev/null
+rm -r /var/lib/bitlbee/* >/dev/null 2>/dev/null
+sudo make uninstall-etc >/dev/null 2>/dev/null
+
+set -e
+sudo make install-etc
+(BITLBEE_DEBUG=1 ./bitlbee -Dnv 2> ./debuglog) &
Index: .tests/setup_prosody.sh
===================================================================
--- .tests/setup_prosody.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/setup_prosody.sh	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+sudo prosodyctl deluser test1@localhost >/dev/null 2>/dev/null
+sudo prosodyctl deluser test2@localhost >/dev/null 2>/dev/null
+sudo prosodyctl stop >/dev/null 2>/dev/null
+
+set -e
+sudo /etc/init.d/prosody start
+sudo prosodyctl register test1 localhost asd
+sudo prosodyctl register test2 localhost asd
Index: .tests/talk_to_bitlbee.py
===================================================================
--- .tests/talk_to_bitlbee.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/talk_to_bitlbee.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def talk_to_bitlbee(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.yes_test(clis)
+    return ret
+
+btlib.perform_test(talk_to_bitlbee)
Index: .tests/unregister.py
===================================================================
--- .tests/unregister.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
+++ .tests/unregister.py	(revision 21a328abae2fd91768c8818f578133e59524be73)
@@ -0,0 +1,9 @@
+import btlib
+
+def unregister(clis):
+    ret = True
+    ret = ret & btlib.connect_test(clis)
+    ret = ret & btlib.unregister_test(clis)
+    return ret
+
+btlib.perform_test(unregister)
