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,45 @@ 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 args .test_case_filter :
80+ now = datetime .datetime .utcnow ()
81+
82+ six_months_ago = now + datetime .timedelta (days = - 30 * 6 )
83+ if not startservers .start (fakeclock = fakeclock (six_months_ago ), coverage_dir = args .coverage_dir ):
84+ raise (Exception ("startservers failed (mocking six months ago)" ))
85+ setup_six_months_ago ()
86+ startservers .stop ()
87+
88+ twenty_days_ago = now + datetime .timedelta (days = - 20 )
89+ if not startservers .start (fakeclock = fakeclock (twenty_days_ago ), coverage_dir = args .coverage_dir ):
90+ raise (Exception ("startservers failed (mocking twenty days ago)" ))
91+ setup_twenty_days_ago ()
92+ startservers .stop ()
93+
94+ if not startservers .start (fakeclock = None , coverage_dir = args .coverage_dir ):
7795 raise (Exception ("startservers failed" ))
7896
7997 if args .run_chisel :
8098 run_chisel (args .test_case_filter )
8199
82100 if args .run_go :
83101 run_go_tests (args .test_case_filter , False )
84-
102+
85103 if args .run_go_verbose :
86104 run_go_tests (args .test_case_filter , True )
87105
@@ -94,6 +112,10 @@ def main():
94112 run_cert_checker ()
95113 check_balance ()
96114
115+ # If coverage is enabled, process the coverage data
116+ if args .coverage :
117+ process_covdata (args .coverage_dir )
118+
97119 if not startservers .check ():
98120 raise (Exception ("startservers.check failed" ))
99121
@@ -128,12 +150,24 @@ def check_balance():
128150 ]
129151 for address in addresses :
130152 metrics = requests .get ("http://%s/metrics" % address )
131- if not "grpc_server_handled_total" in metrics .text :
153+ if "grpc_server_handled_total" not in metrics .text :
132154 raise (Exception ("no gRPC traffic processed by %s; load balancing problem?" )
133155 % address )
134156
135157def run_cert_checker ():
136158 run (["./bin/boulder" , "cert-checker" , "-config" , "%s/cert-checker.json" % config_dir ])
137159
160+ def process_covdata (coverage_dir ):
161+ """Process coverage data and generate reports."""
162+ if not os .path .exists (coverage_dir ):
163+ raise (Exception ("Coverage directory does not exist: %s" % coverage_dir ))
164+
165+ # Generate text report
166+ coverage_dir = os .path .abspath (coverage_dir )
167+ cov_text = os .path .join (coverage_dir , "integration.coverprofile" )
168+ # this works, but if it takes a long time consider merging with `go tool covdata merge` first
169+ # https://go.dev/blog/integration-test-coverage#merging-raw-profiles-with-go-tool-covdata-merge
170+ run (["go" , "tool" , "covdata" , "textfmt" , "-i" , coverage_dir , "-o" , cov_text ])
171+
138172if __name__ == "__main__" :
139173 main ()
0 commit comments