Repository: python-tap/tappy

Issue:
Make parser understand YAML diagnostics for TAP version 13
TAP version 13 added optional YAML blocks to tests. Everything else about the spec stayed the same. If the TAP parser was updated to support YAML blocks, then tappy could be considered version 13 "compliant" (even though YAML blocks never had any formally defined schema).


Discussion:
arewm: What is AppVeyor? I do not know why `ModuleNotFoundError` is not defined, isn't that part of the standard library?
codecov-io: # [Codecov](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=h1) Report
> Merging [#70](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=desc) into [master](https://codecov.io/gh/python-tap/tappy/commit/540a1cb8d37192bc455408a15e1a331cac135065?src=pr&el=desc) will **decrease** coverage by `32.44%`.
> The diff coverage is `13.63%`.

[![Impacted file tree graph](https://codecov.io/gh/python-tap/tappy/pull/70/graphs/tree.svg?width=650&height=150&src=pr&token=ULVfoGuR2p)](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           master      #70       +/-   ##
===========================================
- Coverage     100%   67.55%   -32.45%     
===========================================
  Files          12       11        -1     
  Lines         483      490        +7     
===========================================
- Hits          483      331      -152     
- Misses          0      159      +159
```


| [Impacted Files](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tap/line.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL2xpbmUucHk=) | `85.71% <100%> (-14.29%)` | :arrow_down: |
| [tap/parser.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL3BhcnNlci5weQ==) | `8.51% <11.62%> (-91.49%)` | :arrow_down: |
| [tap/loader.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL2xvYWRlci5weQ==) | `7.27% <0%> (-92.73%)` | :arrow_down: |
| [tap/adapter.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL2FkYXB0ZXIucHk=) | `76.92% <0%> (-23.08%)` | :arrow_down: |
| [tap/rules.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL3J1bGVzLnB5) | `88.88% <0%> (-11.12%)` | :arrow_down: |
| [tap/main.py](https://codecov.io/gh/python-tap/tappy/pull/70/diff?src=pr&el=tree#diff-dGFwL21haW4ucHk=) | | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=footer). Last update [540a1cb...5d3e356](https://codecov.io/gh/python-tap/tappy/pull/70?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

arewm: All tests are finally passing. Code coverage reduction is due to inability to test the import statement failing. 
arewm: FYI, this implementation does break the concept of parsing output "line-by-line", however, I do not think there is any way to get around it. When parsing a result line, if there is a following yaml block, all of those lines will be parsed.

I do not know if this change needs to be documented anywhere.
arewm: Sounds good @mblayman. I will be happy to look at the final merge. I updated the version but did not put in a release date.

FYI, I despise unicode issues.
mblayman: @arewm sorry for the long delay. Somehow I missed that you addressed issues. I'll give this another look when I have time. It will probably be some time next week when I'm at PyCon.

Patch:
diff --git a/.gitignore b/.gitignore
index 39ec5ab..7755710 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@ pip-log.txt
 nosetests.xml
 htmlcov
 .cache
+.pytest_cache
 
 # Translations
 .transifex.ini
@@ -38,8 +39,9 @@ htmlcov
 .project
 .pydevproject
 
-# Vim
+# Dev
 *.swp
+.vscode
 
 # TAP
 *.tap
diff --git a/.travis.yml b/.travis.yml
index de124bc..3835e8a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,6 +25,9 @@ matrix:
         - brew upgrade python
         - python3 -m venv venv
         - source venv/bin/activate
+    - os: linux
+      python: 3.6
+      env: TOX_ENV=with_optional
     - os: linux
       python: 2.7
       env: TOX_ENV=runner
diff --git a/Pipfile b/Pipfile
index 0b7bf1d..8499c0f 100644
--- a/Pipfile
+++ b/Pipfile
@@ -16,7 +16,8 @@ Sphinx = "*"
 tox = "*"
 twine = "*"
 pytest = "*"
-
+more-itertools = "*"
+pyyaml = "*"
 
 [packages]
 
diff --git a/Pipfile.lock b/Pipfile.lock
index 85019cc..bbc729b 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,20 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "f8c87d38088c4cd0c495b52eeeb100c09fa7d526e11f4ed07d203b6391bd7bed"
-        },
-        "host-environment-markers": {
-            "implementation_name": "cpython",
-            "implementation_version": "3.6.2",
-            "os_name": "posix",
-            "platform_machine": "x86_64",
-            "platform_python_implementation": "CPython",
-            "platform_release": "15.6.0",
-            "platform_system": "Darwin",
-            "platform_version": "Darwin Kernel Version 15.6.0: Mon Nov 13 21:58:35 PST 2017; root:xnu-3248.72.11~1/RELEASE_X86_64",
-            "python_full_version": "3.6.2",
-            "python_version": "3.6",
-            "sys_platform": "darwin"
+            "sha256": "ab46296ff06c5478af07e61bc3f32d5688d7917c73249df8c89e6413e5473d3a"
         },
         "pipfile-spec": 6,
         "requires": {},
@@ -37,130 +24,104 @@
         },
         "attrs": {
             "hashes": [
-                "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
-                "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
+                "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
+                "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
             ],
             "version": "==17.4.0"
         },
         "babel": {
             "hashes": [
-                "sha256:f20b2acd44f587988ff185d8949c3e208b4b3d5d20fcab7d91fe481ffa435528",
-                "sha256:6007daf714d0cd5524bbe436e2d42b3c20e68da66289559341e48d2cd6d25811"
+                "sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14",
+                "sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80"
             ],
-            "version": "==2.5.1"
+            "index": "pypi",
+            "version": "==2.5.3"
         },
         "certifi": {
             "hashes": [
-                "sha256:244be0d93b71e93fc0a0a479862051414d0e00e16435707e5bf5000f92e04694",
-                "sha256:5ec74291ca1136b40f0379e1128ff80e866597e4e2c1e755739a913bbc3613c0"
+                "sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296",
+                "sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d"
             ],
-            "version": "==2017.11.5"
+            "version": "==2018.1.18"
         },
         "chardet": {
             "hashes": [
-                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
-                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
+                "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+                "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
             ],
             "version": "==3.0.4"
         },
-        "configparser": {
-            "hashes": [
-                "sha256:5308b47021bc2340965c371f0f058cc6971a04502638d4244225c49d80db273a"
-            ],
-            "markers": "python_version < '3.2'",
-            "version": "==3.5.0"
-        },
         "coverage": {
             "hashes": [
-                "sha256:d1ee76f560c3c3e8faada866a07a32485445e16ed2206ac8378bd90dadffb9f0",
-                "sha256:007eeef7e23f9473622f7d94a3e029a45d55a92a1f083f0f3512f5ab9a669b05",
-                "sha256:17307429935f96c986a1b1674f78079528833410750321d22b5fb35d1883828e",
-                "sha256:845fddf89dca1e94abe168760a38271abfc2e31863fbb4ada7f9a99337d7c3dc",
-                "sha256:3f4d0b3403d3e110d2588c275540649b1841725f5a11a7162620224155d00ba2",
-                "sha256:4c4f368ffe1c2e7602359c2c50233269f3abe1c48ca6b288dcd0fb1d1c679733",
-                "sha256:f8c55dd0f56d3d618dfacf129e010cbe5d5f94b6951c1b2f13ab1a2f79c284da",
-                "sha256:cdd92dd9471e624cd1d8c1a2703d25f114b59b736b0f1f659a98414e535ffb3d",
-                "sha256:2ad357d12971e77360034c1596011a03f50c0f9e1ecd12e081342b8d1aee2236",
-                "sha256:e9a0e1caed2a52f15c96507ab78a48f346c05681a49c5b003172f8073da6aa6b",
-                "sha256:eea9135432428d3ca7ee9be86af27cb8e56243f73764a9b6c3e0bda1394916be",
-                "sha256:700d7579995044dc724847560b78ac786f0ca292867447afda7727a6fbaa082e",
-                "sha256:66f393e10dd866be267deb3feca39babba08ae13763e0fc7a1063cbe1f8e49f6",
-                "sha256:5ff16548492e8a12e65ff3d55857ccd818584ed587a6c2898a9ebbe09a880674",
-                "sha256:d00e29b78ff610d300b2c37049a41234d48ea4f2d2581759ebcf67caaf731c31",
-                "sha256:87d942863fe74b1c3be83a045996addf1639218c2cb89c5da18c06c0fe3917ea",
-                "sha256:358d635b1fc22a425444d52f26287ae5aea9e96e254ff3c59c407426f44574f4",
-                "sha256:81912cfe276e0069dca99e1e4e6be7b06b5fc8342641c6b472cb2fed7de7ae18",
-                "sha256:079248312838c4c8f3494934ab7382a42d42d5f365f0cf7516f938dbb3f53f3f",
-                "sha256:b0059630ca5c6b297690a6bf57bf2fdac1395c24b7935fd73ee64190276b743b",
-                "sha256:493082f104b5ca920e97a485913de254cbe351900deed72d4264571c73464cd0",
-                "sha256:e3ba9b14607c23623cf38f90b23f5bed4a3be87cbfa96e2e9f4eabb975d1e98b",
-                "sha256:82cbd3317320aa63c65555aa4894bf33a13fb3a77f079059eb5935eea415938d",
-                "sha256:9721f1b7275d3112dc7ccf63f0553c769f09b5c25a26ee45872c7f5c09edf6c1",
-                "sha256:bd4800e32b4c8d99c3a2c943f1ac430cbf80658d884123d19639bcde90dad44a",
-                "sha256:f29841e865590af72c4b90d7b5b8e93fd560f5dea436c1d5ee8053788f9285de",
-                "sha256:f3a5c6d054c531536a83521c00e5d4004f1e126e2e2556ce399bef4180fbe540",
-                "sha256:dd707a21332615108b736ef0b8513d3edaf12d2a7d5fc26cd04a169a8ae9b526",
-                "sha256:2e1a5c6adebb93c3b175103c2f855eda957283c10cf937d791d81bef8872d6ca",
-                "sha256:f87f522bde5540d8a4b11df80058281ac38c44b13ce29ced1e294963dd51a8f8",
-                "sha256:a7cfaebd8f24c2b537fa6a271229b051cdac9c1734bb6f939ccfc7c055689baa",
-                "sha256:309d91bd7a35063ec7a0e4d75645488bfab3f0b66373e7722f23da7f5b0f34cc",
-                "sha256:0388c12539372bb92d6dde68b4627f0300d948965bbb7fc104924d715fdc0965",
-                "sha256:ab3508df9a92c1d3362343d235420d08e2662969b83134f8a97dc1451cbe5e84",
-                "sha256:43a155eb76025c61fc20c3d03b89ca28efa6f5be572ab6110b2fb68eda96bfea",
-                "sha256:f98b461cb59f117887aa634a66022c0bd394278245ed51189f63a036516e32de",
-                "sha256:b6cebae1502ce5b87d7c6f532fa90ab345cfbda62b95aeea4e431e164d498a3d",
-                "sha256:a4497faa4f1c0fc365ba05eaecfb6b5d24e3c8c72e95938f9524e29dadb15e76",
-                "sha256:2b4d7f03a8a6632598cbc5df15bbca9f778c43db7cf1a838f4fa2c8599a8691a",
-                "sha256:1afccd7e27cac1b9617be8c769f6d8a6d363699c9b86820f40c74cfb3328921c"
-            ],
-            "version": "==4.4.2"
+                "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba",
+                "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed",
+                "sha256:104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a",
+                "sha256:15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd",
+                "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640",
+                "sha256:1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2",
+                "sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162",
+                "sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508",
+                "sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249",
+                "sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694",
+                "sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a",
+                "sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287",
+                "sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1",
+                "sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000",
+                "sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1",
+                "sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e",
+                "sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5",
+                "sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062",
+                "sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba",
+                "sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc",
+                "sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc",
+                "sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99",
+                "sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653",
+                "sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c",
+                "sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558",
+                "sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f",
+                "sha256:9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4",
+                "sha256:ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91",
+                "sha256:b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d",
+                "sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9",
+                "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd",
+                "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d",
+                "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6",
+                "sha256:e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77",
+                "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80",
+                "sha256:f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e"
+            ],
+            "index": "pypi",
+            "version": "==4.5.1"
         },
         "docutils": {
             "hashes": [
-                "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6",
                 "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
-                "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274"
+                "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
+                "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
             ],
             "version": "==0.14"
         },
-        "enum34": {
-            "hashes": [
-                "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
-                "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
-                "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1",
-                "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850"
-            ],
-            "markers": "python_version < '3.4'",
-            "version": "==1.1.6"
-        },
         "flake8": {
             "hashes": [
-                "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37",
-                "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0"
+                "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0",
+                "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37"
             ],
+            "index": "pypi",
             "version": "==3.5.0"
         },
-        "funcsigs": {
-            "hashes": [
-                "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
-                "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
-            ],
-            "markers": "python_version < '3.3'",
-            "version": "==1.0.2"
-        },
         "idna": {
             "hashes": [
-                "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
-                "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
+                "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
+                "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4"
             ],
             "version": "==2.6"
         },
         "imagesize": {
             "hashes": [
-                "sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb",
-                "sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062"
+                "sha256:3620cc0cadba3f7475f9940d22431fc4d407269f1be59ec9b8edcca26440cf18",
+                "sha256:5b326e4678b6925158ccc66a9fa3122b6106d7c876ee32d7de6ce59385b96315"
             ],
-            "version": "==0.7.1"
+            "version": "==1.0.0"
         },
         "jinja2": {
             "hashes": [
@@ -187,21 +148,38 @@
                 "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
                 "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"
             ],
+            "index": "pypi",
             "version": "==2.0.0"
         },
+        "more-itertools": {
+            "hashes": [
+                "sha256:0dd8f72eeab0d2c3bd489025bb2f6a1b8342f9b198f6fc37b52d15cfa4531fea",
+                "sha256:11a625025954c20145b37ff6309cd54e39ca94f72f6bb9576d1195db6fa2442e",
+                "sha256:c9ce7eccdcb901a2c75d326ea134e0886abfbea5f93e91cc95de9507c0816c44"
+            ],
+            "index": "pypi",
+            "version": "==4.1.0"
+        },
+        "packaging": {
+            "hashes": [
+                "sha256:e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0",
+                "sha256:f019b770dd64e585a99714f1fd5e01c7a8f11b45635aa953fd41c689a657375b"
+            ],
+            "version": "==17.1"
+        },
         "pbr": {
             "hashes": [
-                "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac",
-                "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1"
+                "sha256:4e8a0ed6a8705a26768f4c3da26026013b157821fe5f95881599556ea9d91c19",
+                "sha256:dae4aaa78eafcad10ce2581fc34d694faa616727837fd8e55c1a00951ad6744f"
             ],
-            "version": "==3.1.1"
+            "version": "==4.0.2"
         },
         "pkginfo": {
             "hashes": [
-                "sha256:31a49103180ae1518b65d3f4ce09c784e2bc54e338197668b4fb7dc539521024",
-                "sha256:bb1a6aeabfc898f5df124e7e00303a5b3ec9a489535f346bfbddb081af93f89e"
+                "sha256:5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474",
+                "sha256:a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee"
             ],
-            "version": "==1.4.1"
+            "version": "==1.4.2"
         },
         "pluggy": {
             "hashes": [
@@ -211,15 +189,17 @@
         },
         "py": {
             "hashes": [
-                "sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f",
-                "sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d"
+                "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881",
+                "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a"
             ],
-            "version": "==1.5.2"
+            "version": "==1.5.3"
         },
         "pycodestyle": {
             "hashes": [
-                "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9",
-                "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766"
+                "sha256:1ec08a51c901dfe44921576ed6e4c1f5b7ecbad403f871397feedb5eb8e4fa14",
+                "sha256:5ff2fbcbab997895ba9ead77e1b38b3ebc2e5c3b8a6194ef918666e4c790a00e",
+                "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
+                "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
             ],
             "version": "==2.3.1"
         },
@@ -237,32 +217,59 @@
             ],
             "version": "==2.2.0"
         },
+        "pyparsing": {
+            "hashes": [
+                "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04",
+                "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07",
+                "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18",
+                "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e",
+                "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5",
+                "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58",
+                "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010"
+            ],
+            "version": "==2.2.0"
+        },
         "pytest": {
             "hashes": [
-                "sha256:b84878865558194630c6147f44bdaef27222a9f153bbd4a08908b16bf285e0b1",
-                "sha256:53548280ede7818f4dc2ad96608b9f08ae2cc2ca3874f2ceb6f97e3583f25bc4"
+                "sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c",
+                "sha256:fae491d1874f199537fd5872b5e1f0e74a009b979df9d53d1553fd03da1703e1"
             ],
-            "version": "==3.3.2"
+            "index": "pypi",
+            "version": "==3.5.0"
         },
         "pytz": {
             "hashes": [
-                "sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
-                "sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
-                "sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
-                "sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
-                "sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
-                "sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
-                "sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
-                "sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
-                "sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
+                "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555",
+                "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749"
             ],
-            "version": "==2017.3"
+            "version": "==2018.4"
+        },
+        "pyyaml": {
+            "hashes": [
+                "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
+                "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
+                "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
+                "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
+                "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
+                "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
+                "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7",
+                "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
+                "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
+                "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
+                "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
+                "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
+                "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
+                "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269"
+            ],
+            "index": "pypi",
+            "version": "==3.12"
         },
         "requests": {
             "hashes": [
                 "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
                 "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
             ],
+            "index": "pypi",
             "version": "==2.18.4"
         },
         "requests-toolbelt": {
@@ -274,61 +281,55 @@
         },
         "six": {
             "hashes": [
-                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
-                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
+                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
+                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
             ],
             "version": "==1.11.0"
         },
         "snowballstemmer": {
             "hashes": [
-                "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89",
-                "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128"
+                "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
+                "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
             ],
             "version": "==1.2.1"
         },
         "sphinx": {
             "hashes": [
-                "sha256:fdf77f4f30d84a314c797d67fe7d1b46665e6c48a25699d7bf0610e05a2221d4",
-                "sha256:c6de5dbdbb7a0d7d2757f4389cc00e8f6eb3c49e1772378967a12cfcf2cfe098"
+                "sha256:5a1c9a0fec678c24b9a2f5afba240c04668edb7f45c67ce2ed008996b3f21ae2",
+                "sha256:7a606d77618a753adb79e13605166e3cf6a0e5678526e044236fc1ac43650910"
             ],
-            "version": "==1.6.5"
+            "index": "pypi",
+            "version": "==1.7.2"
         },
         "sphinxcontrib-websupport": {
             "hashes": [
-                "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2",
-                "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9"
+                "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9",
+                "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2"
             ],
             "version": "==1.0.1"
         },
         "tox": {
             "hashes": [
-                "sha256:8af30fd835a11f3ff8e95176ccba5a4e60779df4d96a9dfefa1a1704af263225",
-                "sha256:752f5ec561c6c08c5ecb167d3b20f4f4ffc158c0ab78855701a75f5cef05f4b8"
+                "sha256:96efa09710a3daeeb845561ebbe1497641d9cef2ee0aea30db6969058b2bda2f",
+                "sha256:9ee7de958a43806402a38c0d2aa07fa8553f4d2c20a15b140e9f771c2afeade0"
             ],
-            "version": "==2.9.1"
+            "index": "pypi",
+            "version": "==3.0.0"
         },
         "tqdm": {
             "hashes": [
-                "sha256:4c041f8019f7be65b8028ddde9a836f7ccc51c4637f1ff2ba9b5813d38d19d5a",
-                "sha256:df32e6f127dc0ccbc675eadb33f749abbcb8f174c5cb9ec49c0cdb73aa737377"
+                "sha256:059e7dd579f2c1a2b9103a8ec76fb0bc32cc16904d7d65977edfed6b5745dc48",
+                "sha256:a180389a780f6b52268c30f40fdcb0443ab3d925574579d987eadf10c59ff90c"
             ],
-            "version": "==4.19.5"
+            "version": "==4.22.0"
         },
         "twine": {
             "hashes": [
-                "sha256:d3ce5c480c22ccfb761cd358526e862b32546d2fe4bc93d46b5cf04ea3cc46ca",
-                "sha256:caa45b7987fc96321258cd7668e3be2ff34064f5c66d2d975b641adca659c1ab"
+                "sha256:08eb132bbaec40c6d25b358f546ec1dc96ebd2638a86eea68769d9e67fe2b129",
+                "sha256:2fd9a4d9ff0bcacf41fdc40c8cb0cfaef1f1859457c9653fd1b92237cc4e9f25"
             ],
-            "version": "==1.9.1"
-        },
-        "typing": {
-            "hashes": [
-                "sha256:349b1f9c109c84b53ac79ac1d822eaa68fc91d63b321bd9392df15098f746f53",
-                "sha256:63a8255fe7c6269916baa440eb9b6a67139b0b97a01af632e7bd2842e1e02f15",
-                "sha256:d514bd84b284dd3e844f0305ac07511f097e325171f6cc4a20878d11ad771849"
-            ],
-            "markers": "python_version < '3.5'",
-            "version": "==3.6.2"
+            "index": "pypi",
+            "version": "==1.11.0"
         },
         "urllib3": {
             "hashes": [
@@ -339,11 +340,10 @@
         },
         "virtualenv": {
             "hashes": [
-                "sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0",
-                "sha256:02f8102c2436bb03b3ee6dede1919d1dac8a427541652e5ec95171ec8adbc93a"
+                "sha256:1d7e241b431e7afce47e77f8843a276f652699d1fa4f93b9d8ce0076fd7b0b54",
+                "sha256:e8e05d4714a1c51a2f5921e62f547fcb0f713ebbe959e0a7f585cc8bef71d11f"
             ],
-            "markers": "python_version != '3.2'",
-            "version": "==15.1.0"
+            "version": "==15.2.0"
         }
     }
 }
diff --git a/docs/consumers.rst b/docs/consumers.rst
index 91389ad..08f614c 100644
--- a/docs/consumers.rst
+++ b/docs/consumers.rst
@@ -112,6 +112,24 @@ The API specifics are listed below.
 .. autoclass:: tap.parser.Parser
    :members:
 
+TAP version 13
+~~~~~~~~~~~~~~
+
+The specification for version 13 adds support for `yaml blocks <https://testanything.org/tap-version-13-specification.html#yaml-blocks>`_
+to provide additional information about the preceding test. In order to consume
+yaml blocks, ``tappy`` requires `pyyaml <https://pypi.org/project/PyYAML/>`_ and
+`more-itertools <https://pypi.org/project/more-itertools/>`_ to be installed.
+
+These dependencies are optional. If they are not installed, TAP output will still
+be consumed, but any yaml blocks will be parsed as :class:`tap.line.Unknown`. If a
+:class:`tap.line.Result` object has an associated yaml block, :attr:`~tap.line.Result.yaml_block`
+will return the block converted to a ``dict``. Otherwise, it will return ``None``.
+
+``tappy`` provides a strict interpretation of the specification. A yaml block will
+only be associated with a result if it immediately follows that result. Any 
+:class:`diagnostic <tap.line.Diagnostic>` between a :class:`result <tap.line.Result>` and a yaml
+block will result in the block lines being parsed as :class:`tap.line.Unknown`.
+
 Line Categories
 ~~~~~~~~~~~~~~~
 
diff --git a/docs/releases.rst b/docs/releases.rst
index 4eeb635..2eda995 100644
--- a/docs/releases.rst
+++ b/docs/releases.rst
@@ -4,6 +4,8 @@ Releases
 Version 2.3, To Be Released
 ---------------------------
 
+* Make tappy version 13 compliant by adding
+  support for parsing yaml blocks.
 * `unittest.expectedFailure` now uses a TODO directive to better align
   with the specification.
 
diff --git a/tap/__init__.py b/tap/__init__.py
index e90a1e8..9894059 100644
--- a/tap/__init__.py
+++ b/tap/__init__.py
@@ -3,4 +3,4 @@
 from .runner import TAPTestRunner
 
 __all__ = ['TAPTestRunner']
-__version__ = '2.2'
+__version__ = '2.3'
diff --git a/tap/line.py b/tap/line.py
index b38fa31..352bc1b 100644
--- a/tap/line.py
+++ b/tap/line.py
@@ -1,4 +1,9 @@
 # Copyright (c) 2018, Matt Layman and contributors
+try:
+    import yaml
+    LOAD_YAML = True
+except ImportError:  # pragma: no cover
+    LOAD_YAML = False
 
 
 class Line(object):
@@ -16,7 +21,7 @@ class Result(Line):
 
     def __init__(
             self, ok, number=None, description='', directive=None,
-            diagnostics=None):
+            diagnostics=None, raw_yaml_block=None):
         self._ok = ok
         if number:
             self._number = int(number)
@@ -26,6 +31,7 @@ class Result(Line):
         self._description = description
         self.directive = directive
         self.diagnostics = diagnostics
+        self._yaml_block = raw_yaml_block
 
     @property
     def category(self):
@@ -69,6 +75,25 @@ class Result(Line):
         """
         return self.directive.todo
 
+    @property
+    def yaml_block(self):
+        """Lazy load a yaml_block.
+
+        If yaml support is not available,
+        there is an error in parsing the yaml block,
+        or no yaml is associated with this result,
+        ``None`` will be returned.
+
+        :rtype: dict
+        """
+        if LOAD_YAML and self._yaml_block is not None:
+            try:
+                yaml_dict = yaml.load(self._yaml_block)
+                return yaml_dict
+            except yaml.error.YAMLError:
+                print('Error parsing yaml block. Check formatting.')
+        return None
+
     def __str__(self):
         is_not = ''
         if not self.ok:
diff --git a/tap/parser.py b/tap/parser.py
index e28872b..16b3e3c 100644
--- a/tap/parser.py
+++ b/tap/parser.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2018, Matt Layman and contributors
 
 from io import StringIO
+import itertools
 import re
 import sys
 
@@ -8,6 +9,13 @@ from tap.directive import Directive
 from tap.i18n import _
 from tap.line import Bail, Diagnostic, Plan, Result, Unknown, Version
 
+try:
+    from more_itertools import peekable
+    import yaml  # noqa
+    ENABLE_VERSION_13 = True
+except ImportError:  # pragma: no cover
+    ENABLE_VERSION_13 = False
+
 
 class Parser(object):
     """A parser for TAP files and lines."""
@@ -40,8 +48,14 @@ class Parser(object):
     """, re.VERBOSE)
     version = re.compile(r'^TAP version (?P<version>\d+)$')
 
+    yaml_block_start = re.compile(r'^(?P<indent>\s+)-')
+    yaml_block_end = re.compile(r'^\s+\.\.\.')
+
     TAP_MINIMUM_DECLARED_VERSION = 13
 
+    def __init__(self):
+        self._try_peeking = False
+
     def parse_file(self, filename):
         """Parse a TAP file to an iterable of tap.line.Line objects.
 
@@ -73,18 +87,35 @@ class Parser(object):
         stripped from the input lines.
         """
         with fh:
-            for line in fh:
-                yield self.parse_line(line.rstrip())
-
-    def parse_line(self, text):
+            try:
+                first_line = next(fh)
+            except StopIteration:
+                return
+            first_parsed = self.parse_line(first_line.rstrip())
+            fh_new = itertools.chain([first_line], fh)
+            if first_parsed.category == 'version' and \
+                    first_parsed.version >= 13:
+                if ENABLE_VERSION_13:
+                    fh_new = peekable(itertools.chain([first_line], fh))
+                    self._try_peeking = True
+                else:  # pragma no cover
+                    print("""
+WARNING: Optional imports not found, TAP 13 output will be
+    ignored. To parse yaml, see requirements in docs:
+    https://tappy.readthedocs.io/en/latest/consumers.html#tap-version-13""")
+
+            for line in fh_new:
+                yield self.parse_line(line.rstrip(), fh_new)
+
+    def parse_line(self, text, fh=None):
         """Parse a line into whatever TAP category it belongs."""
         match = self.ok.match(text)
         if match:
-            return self._parse_result(True, match)
+            return self._parse_result(True, match, fh)
 
         match = self.not_ok.match(text)
         if match:
-            return self._parse_result(False, match)
+            return self._parse_result(False, match, fh)
 
         if self.diagnostic.match(text):
             return Diagnostic(text)
@@ -114,11 +145,46 @@ class Parser(object):
 
         return Plan(expected_tests, directive)
 
-    def _parse_result(self, ok, match):
+    def _parse_result(self, ok, match, fh=None):
         """Parse a matching result line into a result instance."""
+        peek_match = None
+        try:
+            if fh is not None and self._try_peeking:
+                peek_match = self.yaml_block_start.match(fh.peek())
+        except StopIteration:
+            pass
+        if peek_match is None:
+            return Result(
+                ok,
+                number=match.group('number'),
+                description=match.group('description').strip(),
+                directive=Directive(match.group('directive'))
+            )
+        indent = peek_match.group('indent')
+        concat_yaml = self._extract_yaml_block(indent, fh)
         return Result(
-            ok, match.group('number'), match.group('description').strip(),
-            Directive(match.group('directive')))
+            ok,
+            number=match.group('number'),
+            description=match.group('description').strip(),
+            directive=Directive(match.group('directive')),
+            raw_yaml_block=concat_yaml
+        )
+
+    def _extract_yaml_block(self, indent, fh):
+        """Extract a raw yaml block from a file handler"""
+        raw_yaml = []
+        indent_match = re.compile(r'^{}'.format(indent))
+        try:
+            fh.next()
+            while indent_match.match(fh.peek()):
+                raw_yaml.append(fh.next().replace(indent, '', 1))
+                # check for the end and stop adding yaml if encountered
+                if self.yaml_block_end.match(fh.peek()):
+                    fh.next()
+                    break
+        except StopIteration:
+            pass
+        return '\n'.join(raw_yaml)
 
     def _parse_version(self, match):
         version = int(match.group('version'))
diff --git a/tox.ini b/tox.ini
index 1c3d62d..7551011 100644
--- a/tox.ini
+++ b/tox.ini
@@ -27,6 +27,14 @@ deps =
     pytest
 commands = pytest {envsitepackagesdir}/tap
 
+[testenv:with_optional]
+deps =
+    Babel
+    mock
+    pyyaml
+    more-itertools
+commands = python tap/tests/run.py
+
 [testenv:runner]
 deps =
     Babel
@@ -58,6 +66,8 @@ deps =
     coverage
     mock
     codecov
+    pyyaml
+    more-itertools
 commands =
     coverage run tap/tests/run.py
     coverage report -m --include "*/tap/*" --omit "*/tests/*"


Test patch:
diff --git a/tap/tests/test_loader.py b/tap/tests/test_loader.py
index d90315f..8dcd5e1 100644
--- a/tap/tests/test_loader.py
+++ b/tap/tests/test_loader.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2018, Matt Layman and contributors
 
 import inspect
+from io import StringIO
 import os
 import tempfile
 import unittest
@@ -116,8 +117,8 @@ class TestLoader(TestCase):
         self.assertEqual(
             'Skipping this test file.', suite._tests[0]._line.description)
 
-    @mock.patch('tap.parser.sys')
-    def test_loads_from_stream(self, mock_sys):
+    @mock.patch('tap.parser.sys.stdin', StringIO(u''))
+    def test_loads_from_stream(self):
         loader = Loader()
         suite = loader.load_suite_from_stdin()
         self.assertTrue(isinstance(suite, unittest.TestSuite))
diff --git a/tap/tests/test_parser.py b/tap/tests/test_parser.py
index 92e7aa0..4e919d1 100644
--- a/tap/tests/test_parser.py
+++ b/tap/tests/test_parser.py
@@ -1,6 +1,9 @@
 # Copyright (c) 2018, Matt Layman and contributors
 
+from contextlib import contextmanager
 import inspect
+from io import BytesIO, StringIO
+import sys
 import tempfile
 import unittest
 
@@ -12,6 +15,20 @@ except ImportError:
 from tap.parser import Parser
 
 
+@contextmanager
+def captured_output():
+    if sys.version_info[0] < 3:
+        new_out, new_err = BytesIO(), BytesIO()
+    else:
+        new_out, new_err = StringIO(), StringIO()
+    old_out, old_err = sys.stdout, sys.stderr
+    try:
+        sys.stdout, sys.stderr = new_out, new_err
+        yield sys.stdout, sys.stderr
+    finally:
+        sys.stdout, sys.stderr = old_out, old_err
+
+
 class TestParser(unittest.TestCase):
     """Tests for tap.parser.Parser"""
 
@@ -196,18 +213,215 @@ class TestParser(unittest.TestCase):
         self.assertEqual('plan', lines[0].category)
         self.assertEqual('test', lines[1].category)
         self.assertTrue(lines[1].ok)
+        self.assertIsNone(lines[1].yaml_block)
         self.assertEqual('test', lines[2].category)
         self.assertFalse(lines[2].ok)
 
-    @mock.patch('tap.parser.sys')
-    def test_parses_stdin(self, mock_sys):
-        mock_sys.stdin.__iter__.return_value = iter([
-            '1..2\n',
-            'ok 1 A passing test\n',
-            'not ok 2 A failing test\n',
-        ])
-        mock_sys.stdin.__enter__.return_value = None
-        mock_sys.stdin.__exit__.return_value = None
+    def test_parses_yaml(self):
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+               ---
+               test: sample yaml
+               ...
+            not ok 2 A failing test""")
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_text(sample):
+            lines.append(line)
+
+        try:
+            import yaml
+            from more_itertools import peekable  # noqa
+            converted_yaml = yaml.load(u"""test: sample yaml""")
+            self.assertEqual(4, len(lines))
+            self.assertEqual(13, lines[0].version)
+            self.assertEqual(converted_yaml, lines[2].yaml_block)
+            self.assertEqual('test', lines[3].category)
+            self.assertIsNone(lines[3].yaml_block)
+        except ImportError:
+            self.assertEqual(7, len(lines))
+            self.assertEqual(13, lines[0].version)
+            for l in list(range(3, 6)):
+                self.assertEqual('unknown', lines[l].category)
+            self.assertEqual('test', lines[6].category)
+
+    def test_parses_yaml_no_end(self):
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+               ---
+               test: sample yaml
+            not ok 2 A failing test""")
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_text(sample):
+            lines.append(line)
+
+        try:
+            import yaml
+            from more_itertools import peekable  # noqa
+            converted_yaml = yaml.load(u"""test: sample yaml""")
+            self.assertEqual(4, len(lines))
+            self.assertEqual(13, lines[0].version)
+            self.assertEqual(converted_yaml, lines[2].yaml_block)
+            self.assertEqual('test', lines[3].category)
+            self.assertIsNone(lines[3].yaml_block)
+        except ImportError:
+            self.assertEqual(6, len(lines))
+            self.assertEqual(13, lines[0].version)
+            for l in list(range(3, 5)):
+                self.assertEqual('unknown', lines[l].category)
+            self.assertEqual('test', lines[5].category)
+
+    def test_parses_yaml_more_complex(self):
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+               ---
+               message: test
+               severity: fail
+               data:
+                 got:
+                   - foo
+                 expect:
+                   - bar""")
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_text(sample):
+            lines.append(line)
+
+        try:
+            import yaml
+            from more_itertools import peekable  # noqa
+            converted_yaml = yaml.load(u"""
+               message: test
+               severity: fail
+               data:
+                 got:
+                   - foo
+                 expect:
+                   - bar""")
+            self.assertEqual(3, len(lines))
+            self.assertEqual(13, lines[0].version)
+            self.assertEqual(converted_yaml, lines[2].yaml_block)
+        except ImportError:
+            self.assertEqual(11, len(lines))
+            self.assertEqual(13, lines[0].version)
+            for l in list(range(3, 11)):
+                self.assertEqual('unknown', lines[l].category)
+
+    def test_parses_yaml_no_association(self):
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+            # Diagnostic line
+               ---
+               test: sample yaml
+               ...
+            not ok 2 A failing test""")
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_text(sample):
+            lines.append(line)
+
+        self.assertEqual(8, len(lines))
+        self.assertEqual(13, lines[0].version)
+        self.assertIsNone(lines[2].yaml_block)
+        self.assertEqual('diagnostic', lines[3].category)
+        for l in list(range(4, 7)):
+            self.assertEqual('unknown', lines[l].category)
+        self.assertEqual('test', lines[7].category)
+
+    def test_parses_yaml_no_start(self):
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+               test: sample yaml
+               ...
+            not ok 2 A failing test""")
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_text(sample):
+            lines.append(line)
+
+        self.assertEqual(6, len(lines))
+        self.assertEqual(13, lines[0].version)
+        self.assertIsNone(lines[2].yaml_block)
+        for l in list(range(3, 5)):
+            self.assertEqual('unknown', lines[l].category)
+        self.assertEqual('test', lines[5].category)
+
+    def test_malformed_yaml(self):
+        self.maxDiff = None
+        sample = inspect.cleandoc(
+            u"""TAP version 13
+            1..2
+            ok 1 A passing test
+               ---
+               test: sample yaml
+               \tfail: tabs are not allowed!
+               ...
+            not ok 2 A failing test""")
+        yaml_err = inspect.cleandoc(
+            u"""
+WARNING: Optional imports not found, TAP 13 output will be
+    ignored. To parse yaml, see requirements in docs:
+    https://tappy.readthedocs.io/en/latest/consumers.html#tap-version-13""")
+        parser = Parser()
+        lines = []
+
+        with captured_output() as (parse_out, _):
+            for line in parser.parse_text(sample):
+                lines.append(line)
+
+        try:
+            import yaml  # noqa
+            from more_itertools import peekable  # noqa
+            self.assertEqual(4, len(lines))
+            self.assertEqual(13, lines[0].version)
+            with captured_output() as (out, _):
+                self.assertIsNone(lines[2].yaml_block)
+            self.assertEqual(
+                'Error parsing yaml block. Check formatting.',
+                out.getvalue().strip())
+            self.assertEqual('test', lines[3].category)
+            self.assertIsNone(lines[3].yaml_block)
+        except ImportError:
+            self.assertEqual(8, len(lines))
+            self.assertEqual(13, lines[0].version)
+            for l in list(range(3, 7)):
+                self.assertEqual('unknown', lines[l].category)
+            self.assertEqual('test', lines[7].category)
+            self.assertEqual(
+                yaml_err, parse_out.getvalue().strip())
+
+    def test_parse_empty_file(self):
+        temp = tempfile.NamedTemporaryFile(delete=False)
+        temp.close()
+        parser = Parser()
+        lines = []
+
+        for line in parser.parse_file(temp.name):
+            lines.append(line)
+
+        self.assertEqual(0, len(lines))
+
+    @mock.patch('tap.parser.sys.stdin',
+                StringIO(u"""1..2
+ok 1 A passing test
+not ok 2 A failing test"""))
+    def test_parses_stdin(self):
         parser = Parser()
         lines = []