@@ -88,8 +88,8 @@ stages:
8888
8989 - bash : |
9090 set -e
91- case "$(python -c 'import sys; print(sys.platform)') " in
92- linux )
91+ case "$AGENT_OS " in
92+ Linux )
9393 echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries
9494 sudo apt update
9595 sudo apt install --no-install-recommends \
@@ -103,6 +103,7 @@ stages:
103103 gir1.2-gtk-3.0 \
104104 graphviz \
105105 inkscape \
106+ lcov \
106107 libcairo2 \
107108 libgirepository-1.0-1 \
108109 lmodern \
@@ -116,13 +117,13 @@ stages:
116117 texlive-pictures \
117118 texlive-xetex
118119 ;;
119- darwin )
120+ Darwin )
120121 brew install --cask xquartz
121122 brew install ccache ffmpeg imagemagick mplayer ninja pkg-config
122123 brew tap homebrew/cask-fonts
123124 brew install font-noto-sans-cjk-sc
124125 ;;
125- win32 )
126+ Windows_NT )
126127 choco install ninja
127128 ;;
128129 *)
@@ -139,8 +140,26 @@ stages:
139140 displayName: 'Install dependencies with pip'
140141
141142 - bash : |
143+ case "$AGENT_OS" in
144+ Linux)
145+ export CPPFLAGS='--coverage -fprofile-abs-path'
146+ ;;
147+ Darwin)
148+ export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw'
149+ export CPPFLAGS="$CPPFLAGS -fcoverage-mapping"
150+ ;;
151+ Windows_NT)
152+ CONFIG='--config-settings=setup-args=--vsenv'
153+ CONFIG="$CONFIG --config-settings=setup-args=-Dcpp_link_args=-PROFILE"
154+ CONFIG="$CONFIG --config-settings=setup-args=-Dbuildtype=debug"
155+ ;;
156+ *)
157+ exit 1
158+ ;;
159+ esac
160+
142161 python -m pip install \
143- --no-build-isolation --config-settings=setup-args="--vsenv" \
162+ --no-build-isolation $CONFIG \
144163 --verbose --editable .[dev] ||
145164 [[ "$PYTHON_VERSION" = 'Pre' ]]
146165 displayName: "Install self"
@@ -152,12 +171,109 @@ stages:
152171 displayName : ' print pip'
153172
154173 - bash : |
155- PYTHONFAULTHANDLER=1 python -m pytest --junitxml=junit/test-results.xml -raR --maxfail=50 --timeout=300 --durations=25 --cov-report= --cov=lib -n 2 ||
174+ set -e
175+ if [[ "$AGENT_OS" == 'Windows_NT' ]]; then
176+ SESSION_ID=$(python -c "import uuid; print(uuid.uuid4(), end='')")
177+ echo "Coverage session ID: ${SESSION_ID}"
178+ VS=$(ls -d /c/Program\ Files*/Microsoft\ Visual\ Studio/*/Enterprise)
179+ echo "Visual Studio: ${VS}"
180+ DIR="$VS/Common7/IDE/Extensions/Microsoft/CodeCoverage.Console"
181+ if [[ -d $DIR ]]; then
182+ # This is for MSVC 2022 (on windows-latest).
183+ TOOL="$DIR/Microsoft.CodeCoverage.Console.exe"
184+ for f in build/cp*/src/*.pyd; do
185+ echo $f
186+ echo "=============================="
187+ "$TOOL" instrument $f --session-id $SESSION_ID \
188+ --log-level Verbose --log-file instrument.log
189+ cat instrument.log
190+ rm instrument.log
191+ done
192+ echo "Starting $TOOL in server mode"
193+ "$TOOL" collect \
194+ --session-id $SESSION_ID --server-mode \
195+ --output-format cobertura --output extensions.xml \
196+ --log-level Verbose --log-file extensions.log &
197+ VS_VER=2022
198+ else
199+ DIR="$VS"/Team\ Tools/Dynamic\ Code\ Coverage\ Tools/amd64
200+ if [[ -d $DIR ]]; then
201+ # This is for MSVC 2019 (on windows-2019).
202+ VSINSTR="$VS"/Team\ Tools/Performance\ Tools/vsinstr.exe
203+ for f in build/cp*/src/*.pyd; do
204+ "$VSINSTR" $f -Verbose -Coverage
205+ done
206+ TOOL="$DIR/CodeCoverage.exe"
207+ cat > extensions.config << EOF
208+ <CodeCoverage>
209+ <CollectFromChildProcesses>true</CollectFromChildProcesses>
210+ <ModulePaths>
211+ <Include>
212+ <ModulePath>.*\\.*\.pyd</ModulePath>
213+ </Include>
214+ </ModulePaths>
215+ </CodeCoverage>
216+ EOF
217+ echo "Starting $TOOL in server mode"
218+ "$TOOL" collect \
219+ -config:extensions.config -session:$SESSION_ID \
220+ -output:extensions.coverage -verbose &
221+ echo "Started $TOOL"
222+ VS_VER=2019
223+ fi
224+ fi
225+ echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL"
226+ fi
227+ PYTHONFAULTHANDLER=1 python -m pytest -raR -n 2 \
228+ --maxfail=50 --timeout=300 --durations=25 \
229+ --junitxml=junit/test-results.xml --cov-report=xml --cov=lib ||
156230 [[ "$PYTHON_VERSION" = 'Pre' ]]
231+ if [[ -n $SESSION_ID ]]; then
232+ if [[ $VS_VER == 2022 ]]; then
233+ "$TOOL" shutdown $SESSION_ID
234+ echo "Coverage collection log"
235+ echo "======================="
236+ cat extensions.log
237+ else
238+ "$TOOL" shutdown -session:$SESSION_ID
239+ fi
240+ fi
157241 displayName: 'pytest'
158242
159243 - bash : |
160- bash <(curl -s https://codecov.io/bash) -f "!*.gcov" -X gcov
244+ case "$AGENT_OS" in
245+ Linux)
246+ lcov --rc lcov_branch_coverage=1 --capture --directory . \
247+ --output-file coverage.info
248+ lcov --rc lcov_branch_coverage=1 --output-file coverage.info \
249+ --extract coverage.info $PWD/src/'*' $PWD/lib/'*'
250+ lcov --rc lcov_branch_coverage=1 --list coverage.info
251+ find . -name '*.gc*' -delete
252+ ;;
253+ Darwin)
254+ xcrun llvm-profdata merge -sparse default.*.profraw \
255+ -o default.profdata
256+ xcrun llvm-cov export -format="lcov" build/*/src/*.so \
257+ -instr-profile default.profdata > info.lcov
258+ ;;
259+ Windows_NT)
260+ if [[ -f extensions.coverage ]]; then
261+ # For MSVC 2019.
262+ "$VS_COVERAGE_TOOL" analyze -output:extensions.xml \
263+ -include_skipped_functions -include_skipped_modules \
264+ extensions.coverage
265+ rm extensions.coverage
266+ fi
267+ ;;
268+ *)
269+ exit 1
270+ ;;
271+ esac
272+ displayName: 'Filter C coverage'
273+ - bash : |
274+ bash <(curl -s https://codecov.io/bash) \
275+ -n "$PYTHON_VERSION $AGENT_OS" \
276+ -f 'coverage.xml' -f 'extensions.xml'
161277 displayName: 'Upload to codecov.io'
162278
163279 - task : PublishTestResults@2
0 commit comments