11import gzip
22import io
3+ import json
34import requests
45import tempfile
6+ import zlib
7+ from base64 import b64encode
58from urllib .parse import urljoin
69
710
@@ -23,31 +26,38 @@ def __init__(self, slug, commit=None, branch=None, token=None):
2326 self .commit = commit
2427 self .branch = branch
2528 self .token = token
26- self .store_url = None
27- self ._buffer = io .StringIO ()
29+ self ._coverage_store_url = None
30+ self ._coverage_buffer = io .StringIO ()
31+ self ._test_result_store_url = None
32+ self ._test_result_files = []
2833
29- def write_network_files (self , files ):
30- self ._buffer .write (
34+ def add_network_files (self , files ):
35+ self ._coverage_buffer .write (
3136 '\n ' .join (files + ['<<<<<< network' ])
3237 )
3338
3439 def add_coverage_report (self , cov , filename = 'coverage.xml' , ** kwargs ):
3540 with tempfile .NamedTemporaryFile (mode = 'r' ) as xml_report :
3641 # embed xml report
37- self ._buffer .write (f'\n # path=./{ filename } \n ' )
42+ self ._coverage_buffer .write (f'\n # path=./{ filename } \n ' )
3843 cov .xml_report (outfile = xml_report .name )
3944 xml_report .seek (0 )
40- self ._buffer .write (xml_report .read ())
41- self ._buffer .write ('\n <<<<<< EOF' )
42-
43- def add_junit_xml (self , path ):
44- with open (path , 'r' ) as junit_xml :
45- self ._buffer .write ('\n # path=./junit.xml\n ' )
46- self ._buffer .write (junit_xml .read ())
47- self ._buffer .write ('\n <<<<<< EOF' )
45+ self ._coverage_buffer .write (xml_report .read ())
46+ self ._coverage_buffer .write ('\n <<<<<< EOF' )
47+
48+ def add_junit_xml (self , path , filename = 'junit.xml' ):
49+ with open (path , 'rb' ) as junit_xml :
50+ self ._test_result_files .append ({
51+ 'filename' : filename ,
52+ 'format' : 'base64+compressed' ,
53+ 'data' : b64encode (
54+ zlib .compress (junit_xml .read ())
55+ ).decode ('ascii' ),
56+ 'labels' : '' ,
57+ })
4858
4959 def get_payload (self ):
50- return self ._buffer .getvalue ()
60+ return self ._coverage_buffer .getvalue ()
5161
5262 def ping (self ):
5363 if not self .slug :
@@ -83,10 +93,30 @@ def ping(self):
8393 raise CodecovError (
8494 f'Invalid response from codecov API:\n { response .text } '
8595 )
86- self .store_url = lines [1 ]
96+ self ._coverage_store_url = lines [1 ]
97+
98+ if not self ._test_result_files :
99+ return
100+
101+ headers = {} if self .token is None else {
102+ 'Authorization' : f'token { self .token } ' ,
103+ 'User-Agent' : package ()
104+ }
105+ data = {
106+ 'slug' : self .slug ,
107+ 'branch' : self .branch or '' ,
108+ 'commit' : self .commit or '' ,
109+ }
110+ api_url = urljoin (self .api_endpoint , '/upload/test_results/v1' )
111+ response = requests .post (api_url , headers = headers , json = data )
112+ if response .ok :
113+ # TODO: Fail more loudly?
114+ url = response .json ()['raw_upload_location' ]
115+ if url .startswith (self .storage_endpoint ):
116+ self ._test_result_store_url = url
87117
88118 def upload (self ):
89- if not self .store_url :
119+ if not self ._coverage_store_url :
90120 raise CodecovError ('Need to ping API before upload.' )
91121
92122 headers = {
@@ -98,10 +128,20 @@ def upload(self):
98128 payload .write (self .get_payload ().encode ('utf-8' ))
99129 gz_payload .seek (0 )
100130 response = requests .put (
101- self .store_url , headers = headers , data = gz_payload
131+ self ._coverage_store_url , headers = headers , data = gz_payload
102132 )
103133
104134 if not response .ok :
105135 raise CodecovError ('Failed to upload report to storage endpoint.' )
106136
107- self .store_url = None # NOTE: Invalidate store url after upload
137+ self ._coverage_store_url = None
138+
139+ if not self ._test_result_store_url or not self ._test_result_files :
140+ return
141+
142+ json_payload = json .dumps ({
143+ 'test_result_files' : self ._test_result_files
144+ }).encode ('ascii' )
145+ # TODO: Fail more loudly?
146+ requests .put (self ._test_result_store_url , data = json_payload )
147+ self ._test_result_store_url = None
0 commit comments