github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/gubernator/view_build_test.py (about)

     1  # Copyright 2016 The Kubernetes Authors.
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #     http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  
    15  import os
    16  import unittest
    17  
    18  import cloudstorage as gcs
    19  
    20  import view_build
    21  
    22  import main_test
    23  import gcs_async_test
    24  import github.models
    25  import testgrid_test
    26  
    27  app = main_test.app
    28  init_build = main_test.init_build
    29  write = gcs_async_test.write
    30  
    31  class ParseJunitTest(unittest.TestCase):
    32      @staticmethod
    33      def parse(xml):
    34          parser = view_build.JUnitParser()
    35          parser.parse_xml(xml, 'fp')
    36          return parser.get_results()
    37  
    38      def test_normal(self):
    39          results = self.parse(main_test.JUNIT_SUITE)
    40          stack = '/go/src/k8s.io/kubernetes/test.go:123\nError Goes Here'
    41          self.assertEqual(results, {
    42              'passed': ['Second'],
    43              'skipped': ['First'],
    44              'failed': [('Third', 96.49, stack, "fp", "")],
    45          })
    46  
    47      def test_testsuites(self):
    48          results = self.parse('''
    49              <testsuites>
    50                  <testsuite name="k8s.io/suite">
    51                      <properties>
    52                          <property name="go.version" value="go1.6"/>
    53                      </properties>
    54                      <testcase name="TestBad" time="0.1">
    55                          <failure>something bad</failure>
    56                          <system-out>out: first line</system-out>
    57                          <system-err>err: first line</system-err>
    58                          <system-out>out: second line</system-out>
    59                      </testcase>
    60                  </testsuite>
    61              </testsuites>''')
    62          self.assertEqual(results['failed'], [(
    63              'k8s.io/suite TestBad', 0.1, 'something bad', "fp",
    64              "out: first line\nout: second line\nerr: first line",
    65              )])
    66  
    67      def test_nested_testsuites(self):
    68          results = self.parse('''
    69              <testsuites>
    70                  <testsuite name="k8s.io/suite">
    71                      <testsuite name="k8s.io/suite/sub">
    72                          <properties>
    73                              <property name="go.version" value="go1.6"/>
    74                          </properties>
    75                          <testcase name="TestBad" time="0.1">
    76                              <failure>something bad</failure>
    77                              <system-out>out: first line</system-out>
    78                              <system-err>err: first line</system-err>
    79                              <system-out>out: second line</system-out>
    80                          </testcase>
    81                      </testsuite>
    82                  </testsuite>
    83              </testsuites>''')
    84          self.assertEqual(results['failed'], [(
    85              'k8s.io/suite/sub TestBad', 0.1, 'something bad', "fp",
    86              "out: first line\nout: second line\nerr: first line",
    87              )])
    88  
    89      def test_bad_xml(self):
    90          self.assertEqual(self.parse('''<body />''')['failed'], [])
    91  
    92      def test_corrupt_xml(self):
    93          self.assertEqual(self.parse('<a>\xff</a>')['failed'], [])
    94          failures = self.parse('''
    95              <testsuites>
    96                  <testsuite name="a">
    97                      <testcase name="Corrupt" time="0">
    98                          <failure>something bad \xff</failure>
    99                      </testcase>
   100                  </testsuite>
   101              </testsuites>''')['failed']
   102          self.assertEqual(failures, [('a Corrupt', 0.0, 'something bad ?', 'fp', '')])
   103  
   104      def test_not_xml(self):
   105          failures = self.parse('\x01')['failed']
   106          self.assertEqual(failures,
   107              [(failures[0][0], 0.0, 'not well-formed (invalid token): line 1, column 0', 'fp', '')])
   108  
   109  class BuildTest(main_test.TestBase):
   110      # pylint: disable=too-many-public-methods
   111  
   112      BUILD_DIR = '/kubernetes-jenkins/logs/somejob/1234/'
   113  
   114      def setUp(self):
   115          self.init_stubs()
   116          init_build(self.BUILD_DIR)
   117          testgrid_test.write_config()
   118  
   119      def get_build_page(self, trailing=''):
   120          return app.get('/build' + self.BUILD_DIR + trailing)
   121  
   122      def test_missing(self):
   123          """Test that a missing build gives a 404."""
   124          response = app.get('/build' + self.BUILD_DIR.replace('1234', '1235'),
   125                             status=404)
   126          self.assertIn('1235', response)
   127  
   128      def test_missing_started(self):
   129          """Test that a missing started.json still renders a proper page."""
   130          build_dir = '/kubernetes-jenkins/logs/job-with-no-started/1234/'
   131          init_build(build_dir, started=False)
   132          response = app.get('/build' + build_dir)
   133          self.assertRegexpMatches(response.body, 'Result.*SUCCESS')
   134          self.assertIn('job-with-no-started', response)
   135          self.assertNotIn('Started', response)  # no start timestamp
   136          self.assertNotIn('github.com', response)  # no version => no src links
   137  
   138      def test_missing_finished(self):
   139          """Test that a missing finished.json still renders a proper page."""
   140          build_dir = '/kubernetes-jenkins/logs/job-still-running/1234/'
   141          init_build(build_dir, finished=False)
   142          response = app.get('/build' + build_dir)
   143          self.assertRegexpMatches(response.body, 'Result.*Not Finished')
   144          self.assertIn('job-still-running', response)
   145          self.assertIn('Started', response)
   146  
   147      def test_build(self):
   148          """Test that the build page works in the happy case."""
   149          response = self.get_build_page()
   150          self.assertIn('2014-07-28', response)  # started
   151          self.assertIn('v1+56', response)       # build version
   152          self.assertIn('16m40s', response)      # build duration
   153          self.assertIn('Third', response)       # test name
   154          self.assertIn('1m36s', response)       # test duration
   155          self.assertRegexpMatches(response.body, 'Result.*SUCCESS')
   156          self.assertIn('Error Goes Here', response)
   157          self.assertIn('test.go#L123">', response)  # stacktrace link works
   158  
   159      def test_finished_has_version(self):
   160          """Test that metadata with version in finished works."""
   161          init_build(self.BUILD_DIR, finished_has_version=True)
   162          self.test_build()
   163  
   164      def test_build_no_failures(self):
   165          """Test that builds with no Junit artifacts work."""
   166          gcs.delete(self.BUILD_DIR + 'artifacts/junit_01.xml')
   167          response = self.get_build_page()
   168          self.assertIn('No Test Failures', response)
   169  
   170      def test_show_metadata(self):
   171          write(self.BUILD_DIR + 'started.json',
   172              {
   173                  'version': 'v1+56',
   174                  'timestamp': 1406535800,
   175                  'jenkins-node': 'agent-light-7',
   176                  'pull': 'master:1234,35:abcd',
   177                  'metadata': {
   178                      'master-version': 'm12'
   179                  }
   180              })
   181          write(self.BUILD_DIR + 'finished.json',
   182              {
   183                  'timestamp': 1406536800,
   184                  'result': 'SUCCESS',
   185                  'metadata': {
   186                      'skew-version': 'm11'
   187                  }
   188              })
   189          response = self.get_build_page()
   190          self.assertIn('v1+56', response)
   191          self.assertIn('agent-light-7', response)
   192          self.assertIn('<td>master-version<td>m12', response)
   193          self.assertIn('<td>skew-version<td>m11', response)
   194          self.assertIn('1234', response)
   195          self.assertIn('abcd', response)
   196  
   197      def test_build_show_log(self):
   198          """Test that builds that failed with no failures show the build log."""
   199          gcs.delete(self.BUILD_DIR + 'artifacts/junit_01.xml')
   200          write(self.BUILD_DIR + 'finished.json',
   201                {'result': 'FAILURE', 'timestamp': 1406536800})
   202  
   203          # Unable to fetch build-log.txt, still works.
   204          response = self.get_build_page()
   205          self.assertNotIn('Error lines', response)
   206  
   207          self.testbed.init_memcache_stub()  # clear cached result
   208          write(self.BUILD_DIR + 'build-log.txt',
   209                u'ERROR: test \u039A\n\n\n\n\n\n\n\n\nblah'.encode('utf8'))
   210          response = self.get_build_page()
   211          self.assertIn('Error lines', response)
   212          self.assertIn('No Test Failures', response)
   213          self.assertIn('ERROR</span>: test', response)
   214          self.assertNotIn('blah', response)
   215  
   216      def test_build_show_passed_skipped(self):
   217          response = self.get_build_page()
   218          self.assertIn('First', response)
   219          self.assertIn('Second', response)
   220          self.assertIn('Third', response)
   221          self.assertIn('Show 1 Skipped Tests', response)
   222          self.assertIn('Show 1 Passed Tests', response)
   223  
   224      def test_build_optional_log(self):
   225          write(self.BUILD_DIR + 'build-log.txt', 'error or timeout or something')
   226          response = self.get_build_page()
   227          self.assertIn('<a href="?log#log">', response)
   228          self.assertNotIn('timeout', response)
   229          self.assertNotIn('build-log.txt', response)
   230          response = self.get_build_page('?log')
   231          self.assertIn('timeout', response)
   232          self.assertIn('build-log.txt', response)
   233  
   234      def test_build_testgrid_links(self):
   235          response = self.get_build_page()
   236          base = 'https://k8s-testgrid.appspot.com/k8s#ajob'
   237          self.assertIn('a href="%s"' % base, response)
   238          option = '&amp;include-filter-by-regex=%5EOverall%24%7CThird'
   239          self.assertIn('a href="%s%s"' % (base, option), response)
   240  
   241      def test_build_failure_no_text(self):
   242          # Some failures don't have any associated text.
   243          write(self.BUILD_DIR + 'artifacts/junit_01.xml', '''
   244              <testsuites>
   245                  <testsuite tests="1" failures="1" time="3.274" name="k8s.io/test/integration">
   246                      <testcase classname="integration" name="TestUnschedulableNodes" time="0.210">
   247                          <failure message="Failed" type=""/>
   248                      </testcase>
   249                  </testsuite>
   250              </testsuites>''')
   251          response = self.get_build_page()
   252          self.assertIn('TestUnschedulableNodes', response)
   253          self.assertIn('junit_01.xml', response)
   254  
   255      def test_build_empty_junit(self):
   256          # Sometimes junit files are actually empty (???)
   257          write(self.BUILD_DIR + 'artifacts/junit_01.xml', '')
   258          response = self.get_build_page()
   259          print response
   260          self.assertIn('No Test Failures', response)
   261  
   262      def test_parse_pr_path(self):
   263          def check(prefix, expected):
   264              self.assertEqual(
   265                  view_build.parse_pr_path(gcs_path=prefix,
   266                      default_org='kubernetes',
   267                      default_repo='kubernetes',
   268                  ),
   269                  expected
   270              )
   271  
   272          check('kubernetes-jenkins/pr-logs/pull/123', ('123', '', 'kubernetes/kubernetes'))
   273          check('kubernetes-jenkins/pr-logs/pull/charts/123', ('123', 'charts/', 'kubernetes/charts'))
   274          check(
   275              'kubernetes-jenkins/pr-logs/pull/google_cadvisor/296',
   276              ('296', 'google/cadvisor/', 'google/cadvisor'))
   277  
   278      def test_build_pr_link(self):
   279          ''' The build page for a PR build links to the PR results.'''
   280          build_dir = '/kubernetes-jenkins/pr-logs/pull/123/e2e/567/'
   281          init_build(build_dir)
   282          response = app.get('/build' + build_dir)
   283          self.assertIn('PR #123', response)
   284          self.assertIn('href="/pr/123"', response)
   285  
   286      def test_build_pr_link_other(self):
   287          build_dir = '/kubernetes-jenkins/pr-logs/pull/charts/123/e2e/567/'
   288          init_build(build_dir)
   289          response = app.get('/build' + build_dir)
   290          self.assertIn('PR #123', response)
   291          self.assertIn('href="/pr/charts/123"', response)
   292  
   293      def test_build_xref(self):
   294          '''Test that builds show issues that reference them.'''
   295          github.models.GHIssueDigest.make(
   296              'org/repo', 123, True, True, [],
   297              {'xrefs': [self.BUILD_DIR[:-1]], 'title': 'an update on testing'}, None).put()
   298          response = app.get('/build' + self.BUILD_DIR)
   299          self.assertIn('PR #123', response)
   300          self.assertIn('an update on testing', response)
   301          self.assertIn('org/repo/issues/123', response)
   302  
   303      def test_cache(self):
   304          """Test that caching works at some level."""
   305          response = self.get_build_page()
   306          gcs.delete(self.BUILD_DIR + 'started.json')
   307          gcs.delete(self.BUILD_DIR + 'finished.json')
   308          response2 = self.get_build_page()
   309          self.assertEqual(str(response), str(response2))
   310  
   311      def test_build_directory_redir(self):
   312          build_dir = '/kubernetes-jenkins/pr-logs/directory/somejob/1234'
   313          target_dir = '/kubernetes-jenkins/pr-logs/pull/45/somejob/1234'
   314          write(build_dir + '.txt', 'gs:/' + target_dir)
   315          resp = app.get('/build' + build_dir)
   316          self.assertEqual(resp.status_code, 302)
   317          self.assertEqual(resp.location, 'http://localhost/build' + target_dir)
   318  
   319      def do_view_build_list_test(self, job_dir='/buck/some-job/', indirect=False):
   320          sta_result = {'timestamp': 12345}
   321          fin_result = {'result': 'SUCCESS'}
   322          for n in xrange(120):
   323              write('%s%d/started.json' % (job_dir, n), sta_result)
   324              write('%s%d/finished.json' % (job_dir, n), fin_result)
   325          if indirect:
   326              for n in xrange(120):
   327                  write('%sdirectory/%d.txt' % (job_dir, n), 'gs:/%s%d' % (job_dir, n))
   328  
   329          view_target = job_dir if not indirect else job_dir + 'directory/'
   330  
   331          builds = view_build.build_list(view_target, None)
   332          self.assertEqual(builds,
   333                           [(str(n), '%s%s' % (job_dir, n), sta_result, fin_result)
   334                            for n in range(119, 79, -1)])
   335          # test that ?before works
   336          builds = view_build.build_list(view_target, '80')
   337          self.assertEqual(builds,
   338                           [(str(n), '%s%s' % (job_dir, n), sta_result, fin_result)
   339                            for n in range(79, 39, -1)])
   340  
   341      def test_view_build_list_with_latest(self):
   342          write('/buck/some-job/latest-build.txt', '119')
   343          self.do_view_build_list_test()
   344  
   345      def test_view_build_list_with_old_latest(self):
   346          # latest-build.txt is a hint -- it will probe newer by looking for started.json
   347          write('/buck/some-job/latest-build.txt', '110')
   348          self.do_view_build_list_test()
   349  
   350      def test_view_build_list_no_latest(self):
   351          self.do_view_build_list_test()
   352  
   353      def test_view_build_list_indirect_with_latest(self):
   354          write('/buck/some-job/directory/latest-build.txt', '119')
   355          self.do_view_build_list_test(indirect=True)
   356  
   357      def test_view_build_list_indirect_no_latest(self):
   358          self.do_view_build_list_test(indirect=True)
   359  
   360      def test_build_list_handler(self):
   361          """Test that the job page shows a list of builds."""
   362          response = app.get('/builds' + os.path.dirname(self.BUILD_DIR[:-1]))
   363          self.assertIn('/1234/">1234', response)
   364          self.assertIn('gcsweb', response)
   365  
   366      def test_job_list(self):
   367          """Test that the job list shows our job."""
   368          response = app.get('/jobs/kubernetes-jenkins/logs')
   369          self.assertIn('somejob/">somejob</a>', response)
   370  
   371      def test_recent_runs_across_prs(self):
   372          """Test that "Recent Runs Across PRs" links are correct."""
   373          def expect(path, directory):
   374              response = app.get('/builds/' + path)
   375              self.assertIn('href="/builds/%s"' % directory, response)
   376          # pull request job in main repo
   377          expect(
   378              'k-j/pr-logs/pull/514/pull-kubernetes-unit/',
   379              'k-j/pr-logs/directory/pull-kubernetes-unit')
   380          # pull request jobs in different repos
   381          expect(
   382              'k-j/pr-logs/pull/test-infra/4213/pull-test-infra-bazel',
   383              'k-j/pr-logs/directory/pull-test-infra-bazel')
   384          expect(
   385              'i-p/pull/istio_istio/517/istio-presubmit/',
   386              'i-p/directory/istio-presubmit')