diff --git a/.travis.yml b/.travis.yml
index d5e0f528c8e95856ee05e43b1c81ad7682c70cab..bffddf4dd2e27f5ef9a0d0d8c9f362bfad182aec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,38 +15,42 @@ matrix:
         - os: osx
           language: generic
           env:
-            - TRAVIS_SUDO=false
-            - SCAPY_USE_PCAPDNET=yes
+            - SCAPY_SUDO=false SCAPY_USE_PCAPDNET=yes
 
         # Run as root
         - os: linux
           sudo: required
           python: 2.7
           env:
-            - TRAVIS_SUDO=sudo
+            - SCAPY_SUDO=sudo
 
         - os: linux
           sudo: required
           python: 2.7
           env:
-            - TRAVIS_SUDO=sudo TEST_COMBINED_MODES=yes
+            - SCAPY_SUDO=sudo TEST_COMBINED_MODES=yes SCAPY_COVERAGE=yes 
 
         - os: linux
           sudo: required
           python: 2.7
           env:
-            - TRAVIS_SUDO=sudo SCAPY_USE_PCAPDNET=yes
+            - SCAPY_SUDO=sudo SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes 
 
         - os: osx
           language: generic
           env:
-            - TRAVIS_SUDO=sudo
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
 
         - os: osx
           language: generic
           env:
-            - TRAVIS_SUDO=sudo SCAPY_USE_PCAPDNET=yes
+            - SCAPY_SUDO=sudo SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes
 
 install: bash .travis/install.sh
 
 script: bash .travis/test.sh
+
+after_success:
+    - if [ "$SCAPY_COVERAGE" = "yes" ]; then pip install codecov; fi
+    # With UTScapy, the coverage dot file is located in the test directory
+    - if [ "$SCAPY_COVERAGE" = "yes" ]; then cd test; codecov; fi
diff --git a/.travis/install.sh b/.travis/install.sh
index ee97c6d53c2dfa5147539f457d3ad346d9aaa6fd..55a820288f54d49abae5ef0d9e05c7a6ef403146 100644
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -1,13 +1,13 @@
 # Install dependencies using pip
-if [ -z "$TRAVIS_SUDO" -o "$TRAVIS_SUDO" = "false" ]
+if [ -z "$SCAPY_SUDO" -o "$SCAPY_SUDO" = "false" ]
 then
-  TRAVIS_SUDO=""
+  SCAPY_SUDO=""
   if [ "$TRAVIS_OS_NAME" = "osx" ]
   then
     PIP_INSTALL_FLAGS="--user"
   fi
 fi
-$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS ecdsa mock
+$SCAPY_SUDO pip install $PIP_INSTALL_FLAGS ecdsa mock
 
 # Pycrypto 2.7a1 isn't available on PyPi
 if [ "$TEST_COMBINED_MODES" = "yes" ]
@@ -15,9 +15,15 @@ then
   curl -sL https://github.com/dlitz/pycrypto/archive/v2.7a1.tar.gz | tar xz
   cd pycrypto-2.7a1
   python setup.py build
-  $TRAVIS_SUDO python setup.py install
+  $SCAPY_SUDO python setup.py install
 else
-  $TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto
+  $SCAPY_SUDO pip install $PIP_INSTALL_FLAGS pycrypto
+fi
+
+# Install coverage
+if [ "$SCAPY_COVERAGE" = "yes" ]
+then
+  $SCAPY_SUDO pip install $PIP_INSTALL_FLAGS coverage
 fi
 
 # Install pcap & dnet
@@ -25,7 +31,7 @@ if [ ! -z $SCAPY_USE_PCAPDNET ]
 then
   if [ "$TRAVIS_OS_NAME" = "linux" ]
   then
-    $TRAVIS_SUDO apt-get install python-libpcap python-dumbnet
+    $SCAPY_SUDO apt-get install python-libpcap python-dumbnet
   elif [ "$TRAVIS_OS_NAME" = "osx" ]
   then
     mkdir -p /Users/travis/Library/Python/2.7/lib/python/site-packages
diff --git a/.travis/test.sh b/.travis/test.sh
index d274a258581cff25285f9e783b615a37618861bb..b82d16c8bdf0ca8c6c30ec323506364df22c5d58 100644
--- a/.travis/test.sh
+++ b/.travis/test.sh
@@ -1,15 +1,15 @@
 # Dump environment variables
-echo "TRAVIS_SUDO=" $TRAVIS_SUDO
+echo "SCAPY_SUDO=" $SCAPY_SUDO
 echo "TRAVIS_OS_NAME=" $TRAVIS_OS_NAME
 
 # Dump Scapy config
 python -c "from scapy.all import *; print conf"
 
 # Don't run tests that require root privileges
-if [ -z "$TRAVIS_SUDO" -o "$TRAVIS_SUDO" = "false" ]
+if [ -z "$SCAPY_SUDO" -o "$SCAPY_SUDO" = "false" ]
 then
   UT_FLAGS="-K netaccess -K needs_root"
-  TRAVIS_SUDO=""
+  SCAPY_SUDO=""
 fi
 
 # Test AEAD modes in IPsec if available
@@ -23,6 +23,22 @@ for _path in /sbin /usr/sbin /usr/local/sbin; do
   [ -d "$_path" ] && echo "$PATH" | grep -qvE "(^|:)$_path(:|$)" && export PATH="$PATH:$_path"
 done
 
+# Create a fake Python executable
+if [ "$SCAPY_COVERAGE" = "yes" ]
+then
+  echo '#!/bin/bash' > test/python
+  echo '[ "$*" = "--version" ] && echo "Python 2 - fake version string"' >> test/python
+  echo '[ "$*" != "--version" ] && coverage run -a $*' >> test/python
+  chmod +x test/python
+  PATH=.:$PATH
+
+  # Copy the fake Python interpreter to bypass /etc/sudoers rules on Ubuntu
+  if [ "$SCAPY_SUDO" = "sudo" ]
+  then
+    $SCAPY_SUDO cp test/python /usr/local/sbin/
+  fi
+fi
+
 # Do we have tcpdump?
 which tcpdump >/dev/null 2>&1 || UT_FLAGS+=" -K tcpdump"
 
@@ -36,7 +52,7 @@ if [ "$TRAVIS_OS_NAME" = "osx" ]
 then
   if [ -z $SCAPY_USE_PCAPDNET ]
   then
-    $TRAVIS_SUDO ./run_tests -q -F -t bpf.uts $UT_FLAGS || exit $?
+    $SCAPY_SUDO ./run_tests -q -F -t bpf.uts $UT_FLAGS || exit $?
   fi
 fi
 
@@ -46,10 +62,10 @@ do
   then
     continue
   fi
-  $TRAVIS_SUDO ./run_tests -q -F -t $f $UT_FLAGS || exit $?
+  $SCAPY_SUDO ./run_tests -q -F -t $f $UT_FLAGS || exit $?
 done
 
 for f in ../scapy/contrib/*.uts
 do
-  $TRAVIS_SUDO ./run_tests -f text -t $f $UT_FLAGS -P "load_contrib('$(basename ${f/.uts})')" || exit $?
+  $SCAPY_SUDO ./run_tests -f text -t $f $UT_FLAGS -P "load_contrib('$(basename ${f/.uts})')" || exit $?
 done