1010import argparse
1111import datetime
1212import inspect
13- import json
1413import os
15- import random
1614import re
17- import requests
1815import subprocess
19- import shlex
20- import signal
21- import time
2216
17+ import requests
2318import startservers
24-
2519import v2_integration
2620from helpers import *
2721
28- from acme import challenges
29-
3022# Set the environment variable RACE to anything other than 'true' to disable
3123# race detection. This significantly speeds up integration testing cycles
3224# locally.
@@ -55,6 +47,11 @@ def main():
5547 parser = argparse .ArgumentParser (description = 'Run integration tests' )
5648 parser .add_argument ('--chisel' , dest = "run_chisel" , action = "store_true" ,
5749 help = "run integration tests using chisel" )
50+ parser .add_argument ('--coverage' , dest = "coverage" , action = "store_true" ,
51+ help = "run integration tests with coverage" )
52+ parser .add_argument ('--coverage-dir' , dest = "coverage_dir" , action = "store" ,
53+ default = f"test/coverage/{ datetime .datetime .now ().strftime ('%Y-%m-%d_%H-%M-%S' )} " ,
54+ help = "directory to store coverage data" )
5855 parser .add_argument ('--gotest' , dest = "run_go" , action = "store_true" ,
5956 help = "run Go integration tests" )
6057 parser .add_argument ('--gotestverbose' , dest = "run_go_verbose" , action = "store_true" ,
@@ -64,24 +61,30 @@ def main():
6461 # allow any ACME client to run custom command for integration
6562 # testing (without having to implement its own busy-wait loop)
6663 parser .add_argument ('--custom' , metavar = "CMD" , help = "run custom command" )
67- parser .set_defaults (run_chisel = False , test_case_filter = "" , skip_setup = False )
64+ parser .set_defaults (run_chisel = False , test_case_filter = "" , skip_setup = False , coverage = False , coverage_dir = None )
6865 args = parser .parse_args ()
6966
67+ if args .coverage and args .coverage_dir :
68+ if not os .path .exists (args .coverage_dir ):
69+ os .makedirs (args .coverage_dir )
70+ if not os .path .isdir (args .coverage_dir ):
71+ raise (Exception ("coverage-dir must be a directory" ))
72+
7073 if not (args .run_chisel or args .custom or args .run_go is not None ):
7174 raise (Exception ("must run at least one of the letsencrypt or chisel tests with --chisel, --gotest, or --custom" ))
7275
73- if not startservers .install (race_detection = race_detection ):
76+ if not startservers .install (race_detection = race_detection , coverage = args . coverage ):
7477 raise (Exception ("failed to build" ))
7578
76- if not startservers .start ():
79+ if not startservers .start (coverage_dir = args . coverage_dir ):
7780 raise (Exception ("startservers failed" ))
7881
7982 if args .run_chisel :
8083 run_chisel (args .test_case_filter )
8184
8285 if args .run_go :
8386 run_go_tests (args .test_case_filter , False )
84-
87+
8588 if args .run_go_verbose :
8689 run_go_tests (args .test_case_filter , True )
8790
@@ -94,6 +97,10 @@ def main():
9497 run_cert_checker ()
9598 check_balance ()
9699
100+ # If coverage is enabled, process the coverage data
101+ if args .coverage :
102+ process_covdata (args .coverage_dir )
103+
97104 if not startservers .check ():
98105 raise (Exception ("startservers.check failed" ))
99106
@@ -128,12 +135,24 @@ def check_balance():
128135 ]
129136 for address in addresses :
130137 metrics = requests .get ("http://%s/metrics" % address )
131- if not "grpc_server_handled_total" in metrics .text :
138+ if "grpc_server_handled_total" not in metrics .text :
132139 raise (Exception ("no gRPC traffic processed by %s; load balancing problem?" )
133140 % address )
134141
135142def run_cert_checker ():
136143 run (["./bin/boulder" , "cert-checker" , "-config" , "%s/cert-checker.json" % config_dir ])
137144
145+ def process_covdata (coverage_dir ):
146+ """Process coverage data and generate reports."""
147+ if not os .path .exists (coverage_dir ):
148+ raise (Exception ("Coverage directory does not exist: %s" % coverage_dir ))
149+
150+ # Generate text report
151+ coverage_dir = os .path .abspath (coverage_dir )
152+ cov_text = os .path .join (coverage_dir , "integration.coverprofile" )
153+ # this works, but if it takes a long time consider merging with `go tool covdata merge` first
154+ # https://go.dev/blog/integration-test-coverage#merging-raw-profiles-with-go-tool-covdata-merge
155+ run (["go" , "tool" , "covdata" , "textfmt" , "-i" , coverage_dir , "-o" , cov_text ])
156+
138157if __name__ == "__main__" :
139158 main ()
0 commit comments