diff --git a/README.md b/README.md
index 73989b857f5e0d9ff2200208dd506011845038fa..2c8af6eabe6b7d09beac91da93e71ee054de1474 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,5 @@
-# apparmor-tumbler
+apparmor-tumbler test
 
+Debug output variables:
+
+* DEBUG=1 will enable all output, stdout and stderr (disabled by default)
diff --git a/apparmor-tumbler.yaml b/apparmor-tumbler.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2e62b2032390c13dc8c15714c977615ba9677a03
--- /dev/null
+++ b/apparmor-tumbler.yaml
@@ -0,0 +1,26 @@
+metadata:
+  name: apparmor-tumbler
+  format: "Lava-Test-Shell Test Definition 1.0"
+  description: "Tests that the tumbler AppArmor profile doesn't cause false negatives
+                and that it does not allow arbitrary reading of files in the home directory."
+  maintainer: "luis.araujo@collabora.co.uk"
+  scope:
+  - functional
+  devices:
+  - i386
+  environment:
+  - lava-test-shell
+
+run:
+  steps:
+    - common/run-test-in-systemd --name run-test-sh --timeout 900 ./run-test.sh
+
+parse:
+  fixupdict:
+    FAILED: fail
+    PASSED: pass
+    FAIL: fail
+    PASS: pass
+    SKIP: skip
+    UNKNOWN: unknown
+  pattern: ^(?P<test_case_id>[\w\-\.]+):\s(?P<result>PASS|pass|FAIL|fail|SKIP|skip|UNKNOWN|unknown)$
diff --git a/config.sh b/config.sh
new file mode 100644
index 0000000000000000000000000000000000000000..2f61bbd497c2b276672e04e8d39326257c1474c6
--- /dev/null
+++ b/config.sh
@@ -0,0 +1 @@
+. "${TESTDIR}/common/inherit-config.sh"
diff --git a/resources/media/documents/lorem_ipsum.pdf b/resources/media/documents/lorem_ipsum.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..619776ef3f8f043cba6c6bbae2672e8309823c58
Binary files /dev/null and b/resources/media/documents/lorem_ipsum.pdf differ
diff --git a/resources/media/documents/lorem_presentation.odp b/resources/media/documents/lorem_presentation.odp
new file mode 100644
index 0000000000000000000000000000000000000000..b4df0bc3b10ba542b4a7912c8e1d7036eddd35fc
Binary files /dev/null and b/resources/media/documents/lorem_presentation.odp differ
diff --git a/resources/media/documents/lorem_spreadsheet.ods b/resources/media/documents/lorem_spreadsheet.ods
new file mode 100644
index 0000000000000000000000000000000000000000..9a5833a8db3e2a92d04d3e4220cad5053f7d43cf
Binary files /dev/null and b/resources/media/documents/lorem_spreadsheet.ods differ
diff --git a/resources/media/documents/lorem_text.txt b/resources/media/documents/lorem_text.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8245a605d4972ef13111e20109c15e6560e7911
--- /dev/null
+++ b/resources/media/documents/lorem_text.txt
@@ -0,0 +1,11 @@
+Sed quis felis ac enim condimentum tempus. Suspendisse imperdiet consequat urna
+a ultrices. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
+posuere cubilia Curae; Vestibulum faucibus sem et sem placerat dapibus. In
+dictum elit in dolor porta ullamcorper. Nulla facilisi. Etiam ac neque nunc.
+Donec scelerisque pulvinar feugiat. Nulla congue varius diam rhoncus dapibus.
+Ut metus libero, porttitor vitae vestibulum sed, interdum quis dui. Suspendisse
+sagittis dui at ante semper at semper erat tincidunt. Nullam sed dolor semper
+turpis suscipit congue. Nulla diam ipsum, elementum id fringilla eu, tincidunt
+fringilla lorem. Ut nec est id arcu facilisis bibendum sed vel nisl. Nullam
+venenatis, nisi et ornare scelerisque, enim ligula blandit nunc, a porttitor
+elit tellus eleifend sapien. 
diff --git a/resources/media/documents/more_lorem_ipsum.odt b/resources/media/documents/more_lorem_ipsum.odt
new file mode 100644
index 0000000000000000000000000000000000000000..fa38dd13d149656873667d5666f9166d646d05dd
Binary files /dev/null and b/resources/media/documents/more_lorem_ipsum.odt differ
diff --git a/resources/media/images/320px-European_Common_Frog_Rana_temporaria.jpg b/resources/media/images/320px-European_Common_Frog_Rana_temporaria.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..105bb39903ba68b2a047bc93fb5c78a49eca14ad
Binary files /dev/null and b/resources/media/images/320px-European_Common_Frog_Rana_temporaria.jpg differ
diff --git a/resources/media/images/collabora-logo-big.png b/resources/media/images/collabora-logo-big.png
new file mode 100644
index 0000000000000000000000000000000000000000..8de06b5e720431f0217910eedc2c049360372bcd
Binary files /dev/null and b/resources/media/images/collabora-logo-big.png differ
diff --git a/resources/media/videos/big_buck_bunny_smaller.ogv b/resources/media/videos/big_buck_bunny_smaller.ogv
new file mode 100644
index 0000000000000000000000000000000000000000..010216c906fb2363e600b2c7293e979431c52314
Binary files /dev/null and b/resources/media/videos/big_buck_bunny_smaller.ogv differ
diff --git a/run-test.sh b/run-test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..605ff1b99837ad354cdef6c80b1d65a1f4a5433d
--- /dev/null
+++ b/run-test.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# vim: tw=0
+
+TESTDIR=$(cd $(dirname $0); pwd; cd - >/dev/null 2>&1)
+. "${TESTDIR}/config.sh"
+
+# We want to use the pre-existing session bus.
+export LAUNCH_DBUS="no"
+
+error=0
+"${TESTDIR}"/common/run-aa-test "${TESTDIR}"/tumbler.normal.expected "${TESTDIR}"/tumbler normal || error=$?
+"${TESTDIR}"/common/run-aa-test "${TESTDIR}"/tumbler.malicious.expected "${TESTDIR}"/tumbler malicious || error=$?
+
+if [ $error = 0 ]; then
+  echo "# apparmor-tumbler: all tests passed"
+else
+  echo "# apparmor-tumbler: at least one test failed"
+fi
+
+exit $error
diff --git a/test-tumbler b/test-tumbler
new file mode 100755
index 0000000000000000000000000000000000000000..c393b28dab907f0866a471e80274b9b5bb45cace
--- /dev/null
+++ b/test-tumbler
@@ -0,0 +1,164 @@
+#!/bin/bash
+# vim: set sts=4 sw=4 et tw=0 :
+
+set -e
+
+TESTDIR=$(cd $(dirname $0); pwd; cd - >/dev/null 2>&1)
+. "${TESTDIR}/config.sh"
+
+. "${TESTDIR}/common/update-test-path"
+
+MEDIA_RESOURCE_DIR="$(cd "${TESTDIR}/resources/media" && pwd)"
+
+#########
+# Setup #
+#########
+trap "setup_failure" EXIT
+
+set -x
+# Need a DBus session for this test
+ensure_dbus_session
+set +x
+
+setup_success
+
+###########
+# Execute #
+###########
+_launch_monitor() {
+    # Launch tumblerd manually to allow LD_PRELOAD to work for 
+    # chaiwala-apparmor-tumbler-tests
+    pkill tumblerd || true
+    /usr/lib/*-linux-gnu*/tumbler-1/tumblerd &
+    TUMBLERD_PID=$!
+    sleep 5
+    # Launch and monitor the thumbnailer
+    ${GDBUS} monitor --session --dest "${addr}" --object-path "${obj_path}" >"${logfile}" &
+    GDBUS_MONITOR_PID=$!
+}
+
+_kill_monitor_return() {
+    kill -s TERM $GDBUS_MONITOR_PID || true
+    kill -s TERM $TUMBLERD_PID || true
+    return $1
+}
+
+bash_arrays_to_gvariant_as() {
+    local i converted="['$1'"
+    shift
+    for i in "$@"; do
+        converted+=", '$i'"
+    done
+    converted+="]"
+    echo "${converted}"
+}
+
+_check_uris_have_thumbnail() {
+    local size=$1
+    local uris=$2
+    for i in "${uris[@]}"; do
+        local thumb="${HOME}/.cache/thumbnails/${size}/$(echo -n "${i}" | md5sum | cut -f1 -d\ ).png"
+        say $thumb
+        if [[ ! -f "${thumb}" ]]; then
+            whine "Couldn't find thumbnail $thumb, file $i didn't get thumbnailed!?"
+            ret=1
+        fi
+    done
+}
+
+_generate_thumbnails() {
+    set -x
+    local i ret files logfile addr obj_path method uris filetypes
+    local copy
+    ret=0
+    local size="$1"
+    local special_dir="$2"
+    shift 2
+    files=("$@")
+    logfile="${WORKDIR}/monitor-tumblerd.log"
+    addr="org.freedesktop.thumbnails.Thumbnailer1"
+    obj_path="/org/freedesktop/thumbnails/Thumbnailer1"
+    method="org.freedesktop.thumbnails.Thumbnailer1.Queue"
+    uris=()
+    filetypes=()
+
+    for i in "${files[@]}"; do
+        # Tumbler's AppArmor profile doesn't necessarily let it read the
+        # apertis-tests directory if we're running uninstalled. Copy the
+        # file to a more realistic location, which exercises the AppArmor
+        # profile better anyway.
+        if [ $(( $RANDOM % 2 )) = 0 ] && [ -e "/home/shared/$special_dir" ]; then
+            copy="/home/shared/$special_dir/apertis-tests__$(basename "$i")"
+        else
+            copy="$HOME/$special_dir/apertis-tests__$(basename "$i")"
+        fi
+
+        cp -v "$i" "$copy"
+
+        # Need path relative to /
+        # FIXME: This doesn't do whitespace/special character escaping, etc
+        uris+=("file://$copy")
+        filetypes+=($(file --mime-type "$copy" | cut -d ":" -f 2 | tr -d ' '))
+    done
+
+    # Clear out old thumbnails.
+    rm -rf ${HOME}/.cache/thumbnails/
+
+    _launch_monitor
+
+    # Files to thumbnail
+    ${GDBUS} call --session --dest "${addr}" --object-path "${obj_path}" \
+        --method "${method}" \
+        "$(bash_arrays_to_gvariant_as "${uris[@]}")" \
+        "$(bash_arrays_to_gvariant_as "${filetypes[@]}")" \
+        "${size}" foreground 0
+
+    # Wait for thumbnailing to finish
+    # FIXME: Race condition! Fix me to loop on the monitor log file.
+    _sleep 2
+    _check_uris_have_thumbnail $size $uris || ret=1
+
+    rm -f "/home/shared/$special_dir"/apertis-tests__*
+    rm -f "${HOME}/$special_dir"/apertis-tests__*
+
+    _kill_monitor_return $ret
+    set +x
+}
+
+test_image_normal_thumbnail_generation() {
+    _generate_thumbnails normal Pictures "${MEDIA_RESOURCE_DIR}/images"/*
+}
+
+test_image_large_thumbnail_generation() {
+    _generate_thumbnails large Pictures "${MEDIA_RESOURCE_DIR}/images"/*
+}
+
+test_video_normal_thumbnail_generation() {
+    _generate_thumbnails normal Videos "${MEDIA_RESOURCE_DIR}/videos"/*
+}
+
+test_video_large_thumbnail_generation() {
+    _generate_thumbnails large Videos "${MEDIA_RESOURCE_DIR}/videos"/*
+}
+
+test_document_normal_thumbnail_generation() {
+    _generate_thumbnails normal Documents "${MEDIA_RESOURCE_DIR}/documents"/*.{pdf,odt}
+}
+
+test_document_large_thumbnail_generation() {
+    _generate_thumbnails large Documents "${MEDIA_RESOURCE_DIR}/documents"/*.{pdf,odt}
+}
+
+trap "test_failure" EXIT
+
+# NOTE: Tumbler cannot thumbnail audio files
+src_test_pass <<-EOF
+test_image_normal_thumbnail_generation
+test_image_large_thumbnail_generation
+test_document_normal_thumbnail_generation
+test_document_large_thumbnail_generation
+test_video_normal_thumbnail_generation
+test_video_large_thumbnail_generation
+EOF
+
+test_success
diff --git a/tumbler b/tumbler
new file mode 100755
index 0000000000000000000000000000000000000000..134aea19af0ec142d94cfcfd8e8a9a7375c24987
--- /dev/null
+++ b/tumbler
@@ -0,0 +1,36 @@
+#!/bin/sh
+# vim: set sts=4 sw=4 et tw=0 :
+
+TESTDIR=$(cd $(dirname $0); pwd; cd - >/dev/null 2>&1)
+. "${TESTDIR}/config.sh"
+
+. "${TESTDIR}/common/update-test-path"
+
+if [ $# -ne 1 ] || { [ "$1" != normal ] && [ "$1" != malicious ] ; }
+then
+    echo "Usage: $0 <normal|malicious>"
+    exit 1
+fi
+
+export APERTIS_TESTS_NAME_PREFIX="${1}_"
+
+if [ "$1" = malicious ]; then
+    # Lets copy the system tumbler apparmor profile and modify it to allow
+    # loading the preloaded library
+    TMP_DIR=$(mktemp -d)
+    cp /etc/apparmor.d/usr.lib.tumbler-1.tumblerd ${TMP_DIR}
+    rule="  ${TESTDIR}/${ARCHDIR}/lib/libtumbler-malicious-override.so rm,"
+    rule=$(echo "$rule" | sed 's/\//\\\//g')
+    sed -i ${TMP_DIR}/usr.lib.tumbler-1.tumblerd -e "s/\(\/usr\/lib\/\*\/tumbler-1\/tumblerd {\)/\1\n${rule}/g"
+    echo ">> Loading apparmor profile ${TMP_DIR}/usr.lib.tumbler-1.tumblerd"
+    sudo apparmor_parser -r ${TMP_DIR}/usr.lib.tumbler-1.tumblerd
+
+    LD_PRELOAD="${TESTDIR}/${ARCHDIR}/lib/libtumbler-malicious-override.so" \
+    $TESTDIR/test-tumbler
+
+    echo ">> Reloading apparmor profile /etc/apparmor.d/usr.lib.tumbler-1.tumblerd"
+    sudo apparmor_parser -r /etc/apparmor.d/usr.lib.tumbler-1.tumblerd
+else
+    $TESTDIR/test-tumbler
+fi
+
diff --git a/tumbler.malicious.expected b/tumbler.malicious.expected
new file mode 100644
index 0000000000000000000000000000000000000000..6072727cda9804dbd3b8f97c09904d2a6b64bbde
--- /dev/null
+++ b/tumbler.malicious.expected
@@ -0,0 +1,42 @@
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
+====
+profile:/usr/lib/*/tumbler-1/tumblerd
+sdmode:REJECTING
+denied_mask:r
+operation:open
+name:/home/user/.bash_history
+request_mask:r
diff --git a/tumbler.normal.expected b/tumbler.normal.expected
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391