=== modified file 'dashboard_app/models.py'
--- dashboard_app/models.py	2012-11-07 02:49:00 +0000
+++ dashboard_app/models.py	2012-11-08 23:10:05 +0000
@@ -57,10 +57,10 @@
 
 from dashboard_app.helpers import BundleDeserializer
 from dashboard_app.managers import BundleManager, TestRunDenormalizationManager
-from dashboard_app.repositories import RepositoryItem 
+from dashboard_app.repositories import RepositoryItem
 from dashboard_app.repositories.data_report import DataReportRepository
 from dashboard_app.repositories.data_view import DataViewRepository
-from dashboard_app.signals import bundle_was_deserialized 
+from dashboard_app.signals import bundle_was_deserialized
 
 
 # Fix some django issues we ran into
@@ -737,7 +737,7 @@
         help_text = _(u"Date and time of the commit (optional)"),
         verbose_name = _(u"Commit Timestamp")
     )
-    
+
     def __unicode__(self):
         return _(u"{project_name} from branch {branch_url} at revision {branch_revision}").format(
             project_name=self.project_name, branch_url=self.branch_url, branch_revision=self.branch_revision)
@@ -1009,7 +1009,7 @@
     mime_type = models.CharField(
         verbose_name = _(u"MIME type"),
         max_length = 64)
-    
+
     public_url = models.URLField(
         verbose_name = _(u"Public URL"),
         max_length = 512,
@@ -1053,11 +1053,30 @@
             self.content_type.model == 'testrun'):
             return True
 
+    def is_test_result_attachment(self):
+        if (self.content_type.app_label == 'dashboard_app' and
+            self.content_type.model == 'testresult'):
+            return True
+
     @property
     def test_run(self):
         if self.is_test_run_attachment():
             return self.content_object
 
+    @property
+    def test_result(self):
+        if self.is_test_result_attachment():
+            return self.content_object
+
+    @property
+    def bundle(self):
+        if self.is_test_result_attachment():
+            run = self.test_result.test_run
+        elif self.is_test_run_attachment():
+            run = self.test_run
+        return run.bundle
+
+
     @models.permalink
     def get_absolute_url(self):
         if self.is_test_run_attachment():
@@ -1066,6 +1085,13 @@
                      self.test_run.bundle.content_sha1,
                      self.test_run.analyzer_assigned_uuid,
                      self.pk])
+        elif self.is_test_result_attachment():
+            return ("dashboard_app.views.result_attachment_detail",
+                    [self.test_result.test_run.bundle.bundle_stream.pathname,
+                     self.test_result.test_run.bundle.content_sha1,
+                     self.test_result.test_run.analyzer_assigned_uuid,
+                     self.test_result.relative_index,
+                     self.pk])
 
 
 class TestResult(models.Model):
@@ -1768,7 +1794,7 @@
 
     # given filter:
     # select from testrun
-    #  where testrun.bundle in filter.bundle_streams ^ accessible_bundles 
+    #  where testrun.bundle in filter.bundle_streams ^ accessible_bundles
     #    and testrun has attribute with key = key1 and value = value1
     #    and testrun has attribute with key = key2 and value = value2
     #    and               ...

=== modified file 'dashboard_app/templates/dashboard_app/test_result_detail.html'
--- dashboard_app/templates/dashboard_app/test_result_detail.html	2012-05-28 08:01:53 +0000
+++ dashboard_app/templates/dashboard_app/test_result_detail.html	2012-11-08 22:56:59 +0000
@@ -153,5 +153,17 @@
   {% endif %}
   {% endwith %}
   </dd>
+  <dt>
+    Test result attachments
+  </dt>
+  <dd>
+    <ul>
+      {% for attachment in test_result.attachments.all %}
+        <li>
+          <a href="{{ attachment.get_absolute_url }}">{{ attachment }}</a>
+        </li>
+      {% endfor %}
+    </ul>
+  </dd>
 </dl>
 {% endblock %}

=== modified file 'dashboard_app/urls.py'
--- dashboard_app/urls.py	2012-10-01 08:08:51 +0000
+++ dashboard_app/urls.py	2012-11-08 23:10:05 +0000
@@ -67,6 +67,8 @@
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/attachments$', 'attachment_list'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/attachments/(?P<pk>[0-9]+)/$', 'attachment_detail'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/result/(?P<relative_index>[0-9]+)/$', 'test_result_detail'),
+    url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/result/(?P<relative_index>[0-9]+)/attachments$', 'result_attachment_list'),
+    url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/result/(?P<relative_index>[0-9]+)/attachment/(?P<pk>[0-9]+)$', 'result_attachment_detail'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/hardware-context/$', 'test_run_hardware_context'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)bundles/(?P<content_sha1>[0-9a-z]+)/(?P<analyzer_assigned_uuid>[a-zA-Z0-9-]+)/software-context/$', 'test_run_software_context'),
     url(r'^streams(?P<pathname>/[a-zA-Z0-9/._-]+)test-runs/$', 'test_run_list'),

=== modified file 'dashboard_app/views/__init__.py'
--- dashboard_app/views/__init__.py	2012-10-01 08:08:51 +0000
+++ dashboard_app/views/__init__.py	2012-11-08 23:10:05 +0000
@@ -562,6 +562,31 @@
                 analyzer_assigned_uuid=analyzer_assigned_uuid),
             'test_run': test_run})
 
+@BreadCrumb(
+    "Attachments",
+    parent=test_result_detail,
+    needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid', 'relative_index'])
+def result_attachment_list(request, pathname, content_sha1, analyzer_assigned_uuid, relative_index):
+    test_result = get_restricted_object_or_404(
+        TestResult,
+        lambda test_result: test_result.test_run.bundle.bundle_stream,
+        request.user,
+        test_run__analyzer_assigned_uuid=analyzer_assigned_uuid,
+        relative_index=relative_index,
+    )
+    return object_list(
+        request,
+        queryset=test_result.attachments.all(),
+        template_name="dashboard_app/attachment_list.html",
+        template_object_name="attachment",
+        extra_context={
+            'bread_crumb_trail': BreadCrumbTrail.leading_to(
+                result_attachment_list,
+                pathname=pathname,
+                content_sha1=content_sha1,
+                analyzer_assigned_uuid=analyzer_assigned_uuid,
+                relative_index=relative_index)
+                })
 
 @BreadCrumb(
     "{content_filename}",
@@ -570,7 +595,7 @@
 def attachment_detail(request, pathname, content_sha1, analyzer_assigned_uuid, pk):
     attachment = get_restricted_object_or_404(
         Attachment,
-        lambda attachment: attachment.test_run.bundle.bundle_stream,
+        lambda attachment: attachment.bundle.bundle_stream,
         request.user,
         pk = pk
     )
@@ -587,10 +612,35 @@
         }, RequestContext(request))
 
 
+@BreadCrumb(
+    "{content_filename}",
+    parent=result_attachment_list,
+    needs=['pathname', 'content_sha1', 'analyzer_assigned_uuid', 'relative_index', 'pk'])
+def result_attachment_detail(request, pathname, content_sha1, analyzer_assigned_uuid, relative_index, pk):
+    attachment = get_restricted_object_or_404(
+        Attachment,
+        lambda attachment: attachment.bundle.bundle_stream,
+        request.user,
+        pk = pk
+    )
+    return render_to_response(
+        "dashboard_app/attachment_detail.html", {
+            'bread_crumb_trail': BreadCrumbTrail.leading_to(
+                result_attachment_detail,
+                pathname=pathname,
+                content_sha1=content_sha1,
+                analyzer_assigned_uuid=analyzer_assigned_uuid,
+                relative_index=relative_index,
+                pk=pk,
+                content_filename=attachment.content_filename),
+            "attachment": attachment,
+        }, RequestContext(request))
+
+
 def ajax_attachment_viewer(request, pk):
     attachment = get_restricted_object_or_404(
         Attachment,
-        lambda attachment: attachment.test_run.bundle.bundle_stream,
+        lambda attachment: attachment.bundle.bundle_stream,
         request.user,
         pk=pk
     )

