diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d6710c3b..172f421f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -275,40 +275,3 @@ jobs: - name: Run mutations run: | make test_env.mutation - - - rust: - name: Rust Tests - needs: [build] - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - name: Run install cli - run: | - pip install codecov-cli - - name: Run rust tests - run: | - make test_env.rust_tests - ## Don't upload on forks for now. - - name: upload using codecovcli - if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }} - run: | - codecovcli upload-process --flag rust --token ${{ secrets.CODECOV_ORG_TOKEN }} --fail-on-error - - - name: upload using codecovcli staging - if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }} - run: | - codecovcli -u ${{ secrets.CODECOV_STAGING_URL }} upload-process --flag rust --token ${{ secrets.CODECOV_ORG_TOKEN_STAGING }} --fail-on-error - - - name: upload using codecovcli qa - if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }} - run: | - codecovcli -u ${{ secrets.CODECOV_QA_URL }} upload-process --flag rust --token ${{ secrets.CODECOV_QA_TOKEN }} --fail-on-error - - - name: upload using codecovcli public qa - if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }} - run: | - codecovcli -u ${{ secrets.CODECOV_PUBLIC_QA_URL }} upload-process --flag rust --token ${{ secrets.CODECOV_PUBLIC_QA_TOKEN }} --fail-on-error diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index d63b9a34e..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,550 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.6.5", - "once_cell", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "fraction" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27f0e7512f6915c9bc38594725e33d7673da9308fea0abf4cc258c281cdbb2a" -dependencies = [ - "lazy_static", - "num", -] - -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "indoc" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" - -[[package]] -name = "itoa" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" - -[[package]] -name = "jemalloc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" -dependencies = [ - "cc", - "fs_extra", - "libc", -] - -[[package]] -name = "jemallocator" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" -dependencies = [ - "jemalloc-sys", - "libc", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "proc-macro2" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pyo3" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" -dependencies = [ - "cfg-if", - "indoc", - "libc", - "memoffset 0.9.0", - "parking_lot", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" -dependencies = [ - "once_cell", - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ribs" -version = "0.1.0" -dependencies = [ - "fraction", - "jemallocator", - "pyo3", - "rayon", - "serde", - "serde_json", -] - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "syn" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.12.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" - -[[package]] -name = "unicode-ident" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" - -[[package]] -name = "unindent" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 2aac34cbd..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "ribs" -version = "0.1.0" -authors = ["Thiago Ramos "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } -rayon = "1.1" -fraction = "0.6.3" - -[lib] -name = "ribs" -crate-type = ["cdylib", "rlib"] - -[dependencies.pyo3] -version = "0.19.2" - -[features] -extension-module = ["pyo3/extension-module"] -default = ["extension-module"] - -[target.'cfg(target_env = "musl")'.dependencies.jemallocator] -version = "0.3.0" -features = ["disable_initial_exec_tls"] -profiler = true diff --git a/Makefile b/Makefile index f7afc96d3..431745f8f 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,6 @@ lint.check: requirements.install: python -m venv venv . venv/bin/activate - pip install setuptools_rust pip install -r tests/requirements.txt pip install -r requirements.txt python setup.py develop @@ -46,15 +45,3 @@ test_env.mutation: mutmut run --use-patch-file data.patch || true mkdir /tmp/artifacts; mutmut junitxml > /tmp/artifacts/mut.xml - -test_env.rust_tests: - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly - source $HOME/.cargo/env - sudo apt-get update - sudo apt-get install gcc lsb-release wget software-properties-common - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 15 - RUSTFLAGS="-C instrument-coverage" LLVM_PROFILE_FILE="ribs-%m.profraw" cargo +nightly test --no-default-features - llvm-profdata-15 merge -sparse ribs-*.profraw -o ribs.profdata - llvm-cov-15 show --ignore-filename-regex='/.cargo/registry' --instr-profile=ribs.profdata --object `ls target/debug/deps/ribs-* | grep -v "\.d" | grep -v "\.o"` > app.coverage.txt diff --git a/README.md b/README.md index 689520d7f..4b8dfd6f7 100644 --- a/README.md +++ b/README.md @@ -60,15 +60,6 @@ Remember to add dependencies as loosely as possible. Only make sure to include w Remember that multiple packages, on different contexts of their own requirements, will have to install this. So keeping the requirements loose allow them to avoid version clashes and eases upgrades whenever they need to. -# ribs -Rust Service to be called from inside python - -This is some rust code that is meant to be installed as a python wheel on the repository and used - -It uses [pyo3](https://pyo3.rs) as the binding and [setuptools-rust](https://github.com/PyO3/setuptools-rust) as the tool that turns the rust code into python - -We hope it provides a new level of speed to the CPU-bound parts of the code - ## Contributing This repository, like all of Codecov's repositories, strives to follow our general [Contributing guidlines](https://github.com/codecov/contributing). If you're considering making a contribution to this repository, we encourage review of our Contributing guidelines first. diff --git a/build-wheels.sh b/build-wheels.sh index d8f755877..2db5f684f 100644 --- a/build-wheels.sh +++ b/build-wheels.sh @@ -1,16 +1,13 @@ #!/bin/bash set -ex -curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y -export PATH="$HOME/.cargo/bin:$PATH" - cd /io for PYBIN in /opt/python/cp{35,36,37,38,39}*/bin; do - "${PYBIN}/pip" install -U setuptools wheel setuptools-rust + "${PYBIN}/pip" install -U setuptools wheel "${PYBIN}/python" setup.py bdist_wheel done for whl in dist/*.whl; do auditwheel repair "$whl" -w dist/ -done \ No newline at end of file +done diff --git a/setup.py b/setup.py index 19d8658c2..0e5362cb2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,6 @@ from os import path from setuptools import find_packages, setup -from setuptools_rust import Binding, RustExtension here = path.abspath(path.dirname(__file__)) @@ -12,7 +11,6 @@ setup( name="shared", version="0.11.2", - rust_extensions=[RustExtension("shared.rustyribs", binding=Binding.PyO3)], packages=find_packages(exclude=["contrib", "docs", "tests*"]), # rust extensions are not zip safe, just like C-extensions. zip_safe=False, @@ -49,5 +47,6 @@ "django>=4.2.3", "sqlalchemy==1.*", "ijson==3.*", + "codecov-ribs", ], ) diff --git a/shared/profiling/__init__.py b/shared/profiling/__init__.py index 02c86a5dc..d54cdd10f 100644 --- a/shared/profiling/__init__.py +++ b/shared/profiling/__init__.py @@ -1,7 +1,7 @@ import json from typing import List -from shared.ribs import load_profiling_data +from cc_rustyribs import load_profiling_data def load_profiling(summary_data: dict) -> "ProfilingSummaryDataAnalyzer": diff --git a/shared/reports/changes.py b/shared/reports/changes.py index 76b8ac6eb..beb8d7343 100644 --- a/shared/reports/changes.py +++ b/shared/reports/changes.py @@ -1,12 +1,13 @@ -from shared import ribs +import cc_rustyribs + from shared.reports.types import Change def run_comparison_using_rust(base_report, head_report, diff): - return ribs.run_comparison( + return cc_rustyribs.run_comparison( base_report.rust_report.get_report(), head_report.rust_report.get_report(), - ribs.rustify_diff(diff), + cc_rustyribs.rustify_diff(diff), ) diff --git a/shared/reports/readonly.py b/shared/reports/readonly.py index dae6c5e19..10bd931fd 100644 --- a/shared/reports/readonly.py +++ b/shared/reports/readonly.py @@ -2,6 +2,8 @@ import os import random +from cc_rustyribs import FilterAnalyzer, SimpleAnalyzer, parse_report + from shared.helpers.flag import Flag from shared.metrics import metrics, sentry from shared.reports.resources import ( @@ -10,7 +12,6 @@ ReportTotals, chunks_from_storage_contains_header, ) -from shared.ribs import FilterAnalyzer, SimpleAnalyzer, parse_report from shared.utils.match import match log = logging.getLogger(__name__) diff --git a/shared/ribs/__init__.py b/shared/ribs/__init__.py deleted file mode 100644 index 4b5b1bf2c..000000000 --- a/shared/ribs/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -import json - -from shared.rustyribs import ( - FilterAnalyzer, - ProfilingData, - SimpleAnalyzer, - parse_report, - run_comparison_as_json, -) - - -def run_comparison(*args, **kwargs): - return json.loads(run_comparison_as_json(*args, **kwargs)) - - -def load_profiling_data(data_string): - return ProfilingData.load_from_json(data_string) - - -def rustify_diff(diff): - if diff is None or "files" not in diff: - return {} - new_values = [ - ( - key, - ( - value["type"], - value.get("before"), - [ - ( - tuple(int(x) if x else 0 for x in s["header"]), - [l[0] if l else " " for l in s["lines"]], - ) - for s in value.get("segments", []) - ], - ), - ) - for (key, value) in diff["files"].items() - ] - return dict(new_values) diff --git a/src/analyzers/filter.rs b/src/analyzers/filter.rs deleted file mode 100644 index 7bd424590..000000000 --- a/src/analyzers/filter.rs +++ /dev/null @@ -1,353 +0,0 @@ -use std::collections::HashMap; -use std::collections::HashSet; - -use pyo3::prelude::*; - -use crate::cov; -use crate::diff; -use crate::file; -use crate::line; -use crate::report; - -#[pyclass] -pub struct FilterAnalyzer { - files: Option>, - flags: Option>, -} - -#[pymethods] -impl FilterAnalyzer { - #[new] - fn new(files: Option>, flags: Option>) -> Self { - FilterAnalyzer { files, flags } - } - - pub fn get_totals(&self, report: &report::Report) -> PyResult { - let sessions = match &self.flags { - Some(actual_flags) => Some(report.get_sessions_from_flags(&actual_flags)), - None => None, - }; - let session_count: i32; - let mut initial = report::ReportTotals::new(); - match &sessions { - Some(sess) => { - session_count = sess.len() as i32; - let filtered_totals: Vec = report - .report_files - .iter() - .filter(|(x, _)| self.should_include(x)) - .map(|(_, y)| y.get_filtered_totals(sess)) - .collect(); - for t in filtered_totals { - initial.add_up(&t); - } - } - None => { - session_count = report.session_mapping.len() as i32; - let filtered_totals: Vec = report - .report_files - .iter() - .filter(|(x, _)| self.should_include(x)) - .map(|(_, y)| y.get_totals()) - .collect(); - for t in filtered_totals { - initial.add_up(&t); - } - } - }; - initial.sessions = session_count; - return Ok(initial); - } - - pub fn should_include(&self, filename: &str) -> bool { - match &self.files { - Some(f) => return f.contains(filename), - None => return true, - } - } - - pub fn calculate_diff( - &self, - report: &report::Report, - diff: diff::DiffInput, - ) -> ( - report::ReportTotals, - HashMap, - ) { - let sessions = match &self.flags { - Some(actual_flags) => Some(report.get_sessions_from_flags(&actual_flags)), - None => None, - }; - let mut res = report::ReportTotals::new(); - let mut mapping: HashMap = HashMap::new(); - for (filename, diff_data) in diff.iter() { - if self.should_include(filename) { - match report.get_by_filename(filename) { - None => {} - Some(file_report) => { - let file_res = - self.calculate_reportfile_diff(file_report, diff_data, &sessions); - res.add_up(&file_res.summary); - mapping.insert(filename.to_string(), file_res); - } - } - } - } - return (res, mapping); - } -} - -impl FilterAnalyzer { - fn calculate_reportfile_diff( - &self, - reportfile: &file::ReportFile, - diff_data: &( - String, - Option, - Vec<((i32, i32, i32, i32), Vec)>, - ), - sessions: &Option>, - ) -> diff::FileDiffAnalysis { - let (_, lines_on_head) = diff::get_exclusions_from_diff(Some(&diff_data.2)); - let mut involved_lines: Vec<(i32, line::ReportLine)> = Vec::new(); - for line_number in lines_on_head.iter() { - match reportfile.lines.get(line_number) { - None => {} - Some(line) => { - let possible_filtered_line = match sessions { - Some(sess) => line.filter_by_session_ids(sess), - None => Some(line.clone()), - }; - match possible_filtered_line { - Some(calculated_line) => { - involved_lines.push((*line_number, calculated_line)); - } - None => {} - } - } - } - } - let res = diff::FileDiffAnalysis { - summary: file::FileTotals::from_lines(involved_lines.iter().map(|(_, x)| x).collect()), - lines_with_hits: involved_lines - .iter() - .filter_map(|(line_number, line)| match line.coverage { - cov::Coverage::Hit => Some(*line_number), - _ => None, - }) - .collect(), - lines_with_misses: involved_lines - .iter() - .filter_map(|(line_number, line)| match line.coverage { - cov::Coverage::Miss => Some(*line_number), - _ => None, - }) - .collect(), - lines_with_partials: involved_lines - .iter() - .filter_map(|(line_number, line)| match line.coverage { - cov::Coverage::Partial(_) => Some(*line_number), - _ => None, - }) - .collect(), - }; - return res; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use fraction::GenericFraction; - - #[test] - fn filtered_totals_works() { - let first_file = file::ReportFile { - lines: vec![ - ( - 1, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - ), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new(1, 2)), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }; - let report = report::Report { - report_files: vec![ - ("file1.go".to_string(), first_file), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ( - "plo.c".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let analyzer_unit = FilterAnalyzer { - files: Some(vec!["file1.go".to_string()].into_iter().collect()), - flags: Some(vec!["unit".to_string()]), - }; - let unit_res = analyzer_unit.get_totals(&report).unwrap(); - assert_eq!(unit_res.files, 1); - assert_eq!(unit_res.lines, 2); - assert_eq!(unit_res.hits, 2); - assert_eq!(unit_res.misses, 0); - assert_eq!(unit_res.partials, 0); - assert_eq!(unit_res.sessions, 1); - let analyzer_integration = FilterAnalyzer { - files: Some(vec!["file1.go".to_string()].into_iter().collect()), - flags: Some(vec!["integration".to_string()]), - }; - let integration_res = analyzer_integration.get_totals(&report).unwrap(); - assert_eq!(integration_res.files, 1); - assert_eq!(integration_res.lines, 1); - assert_eq!(integration_res.hits, 0); - assert_eq!(integration_res.misses, 0); - assert_eq!(integration_res.partials, 1); - assert_eq!(integration_res.sessions, 1); - let analyzer_unit_and_integration = FilterAnalyzer { - files: Some(vec!["file1.go".to_string()].into_iter().collect()), - flags: Some(vec!["integration".to_string(), "unit".to_string()]), - }; - let integration_and_unit_res = analyzer_unit_and_integration.get_totals(&report).unwrap(); - assert_eq!(integration_and_unit_res.files, 1); - assert_eq!(integration_and_unit_res.lines, 2); - assert_eq!(integration_and_unit_res.hits, 2); - assert_eq!(integration_and_unit_res.misses, 0); - assert_eq!(integration_and_unit_res.partials, 0); - assert_eq!(integration_and_unit_res.sessions, 2); - let analyzer_apple_and_banana = FilterAnalyzer { - files: Some(vec!["file1.go".to_string()].into_iter().collect()), - flags: Some(vec!["banana".to_string(), "apple".to_string()]), - }; - let apple_and_banana_res = analyzer_apple_and_banana.get_totals(&report).unwrap(); - assert_eq!(apple_and_banana_res.files, 0); - assert_eq!(apple_and_banana_res.lines, 0); - assert_eq!(apple_and_banana_res.hits, 0); - assert_eq!(apple_and_banana_res.misses, 0); - assert_eq!(apple_and_banana_res.partials, 0); - assert_eq!(apple_and_banana_res.sessions, 0); - } - - #[test] - fn filtered_totals_without_flags_works() { - let first_file = file::ReportFile { - lines: vec![ - ( - 1, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - ), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new(1, 2)), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }; - let report = report::Report { - report_files: vec![ - ("file1.go".to_string(), first_file), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ( - "plo.c".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let analyzer = FilterAnalyzer { - files: Some(vec!["file1.go".to_string()].into_iter().collect()), - flags: None, - }; - let unit_res = analyzer.get_totals(&report).unwrap(); - assert_eq!(unit_res.files, 1); - assert_eq!(unit_res.lines, 2); - assert_eq!(unit_res.hits, 2); - assert_eq!(unit_res.misses, 0); - assert_eq!(unit_res.partials, 0); - assert_eq!(unit_res.sessions, 2); - } -} diff --git a/src/analyzers/mod.rs b/src/analyzers/mod.rs deleted file mode 100644 index c43489953..000000000 --- a/src/analyzers/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod filter; -pub mod simple; diff --git a/src/analyzers/simple.rs b/src/analyzers/simple.rs deleted file mode 100644 index 156794783..000000000 --- a/src/analyzers/simple.rs +++ /dev/null @@ -1,102 +0,0 @@ -use pyo3::prelude::*; - -use crate::report; - -#[pyclass] -pub struct SimpleAnalyzer {} - -#[pymethods] -impl SimpleAnalyzer { - #[new] - fn new() -> Self { - SimpleAnalyzer {} - } - - pub fn get_totals(&self, report: &report::Report) -> PyResult { - report.get_simple_totals() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cov; - use crate::file; - use crate::line; - use fraction::GenericFraction; - - #[test] - fn get_totals_works() { - let first_file = file::ReportFile { - lines: vec![ - ( - 1, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - ), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new(1, 2)), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }; - let report = report::Report { - report_files: vec![ - ("file1.go".to_string(), first_file), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ( - "plo.c".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let analyzer_unit = SimpleAnalyzer {}; - let unit_res = analyzer_unit.get_totals(&report).unwrap(); - assert_eq!(unit_res.files, 1); - assert_eq!(unit_res.lines, 2); - assert_eq!(unit_res.hits, 2); - assert_eq!(unit_res.misses, 0); - assert_eq!(unit_res.partials, 0); - } -} diff --git a/src/changes.rs b/src/changes.rs deleted file mode 100644 index f76c219d4..000000000 --- a/src/changes.rs +++ /dev/null @@ -1,1086 +0,0 @@ -use std::collections::HashMap; -use std::collections::HashSet; -use std::iter::FromIterator; - -use rayon::prelude::*; -use serde::Serialize; - -use crate::cov; -use crate::diff; -use crate::file; -use crate::report; - -type LineChange = ((i32, Option), (i32, Option)); - -#[derive(Debug, Serialize)] -pub struct FileChangesAnalysis { - pub base_name: String, - pub head_name: String, - pub file_was_added_by_diff: bool, - pub file_was_removed_by_diff: bool, - pub base_coverage: Option, - pub head_coverage: Option, - pub removed_diff_coverage: Option>, - pub added_diff_coverage: Option>, - pub unexpected_line_changes: Vec, - pub lines_only_on_base: Vec, - pub lines_only_on_head: Vec, -} - -#[derive(Debug, Serialize, PartialEq)] -pub struct ChangePatchTotals { - hits: i32, - misses: i32, - partials: i32, - coverage: Option, -} - -#[derive(Debug, Serialize, PartialEq)] -pub struct ChangeAnalysisSummary { - patch_totals: ChangePatchTotals, -} - -#[derive(Serialize, Debug)] -pub struct ChangeAnalysis { - pub files: Vec, - changes_summary: ChangeAnalysisSummary, -} - -pub fn run_comparison_analysis( - base_report: &report::Report, - head_report: &report::Report, - diff: &diff::DiffInput, -) -> ChangeAnalysis { - let possible_renames: HashMap = diff - .iter() - .map(|(k, v)| (k, &v.1)) - .filter_map(|(k, v)| match v { - None => None, - Some(renamed) => Some((renamed.to_string(), k.to_string())), - }) - .collect(); - let base_filenames_accounted_for_renames: HashSet = - HashSet::from_iter(base_report.report_files.keys().map( - |f| match possible_renames.get(f) { - None => f.to_string(), - Some(v) => v.to_string(), - }, - )); - let head_filenames: HashSet = - HashSet::from_iter(head_report.report_files.keys().map(|f| f.clone())); - let all_filenames: HashSet = base_filenames_accounted_for_renames - .union(&head_filenames) - .map(|f| f.clone()) - .collect(); - let changes_list: Vec = all_filenames - .iter() - .map(|filename| { - let diff_data = diff.get(filename); - let original_name: String = match diff_data { - Some(x) => match &x.1 { - None => filename.to_string(), - Some(o) => o.to_string(), - }, - None => filename.to_string(), - }; - if !base_report.get_by_filename(&original_name).is_none() - || !head_report.get_by_filename(&filename).is_none() - { - run_filereport_analysis( - base_report.get_by_filename(&original_name), - head_report.get_by_filename(&filename), - diff_data, - (&original_name, &filename), - ) - } else { - None - } - }) - .filter_map(|x| x) - .collect(); - return ChangeAnalysis { - changes_summary: produce_summary_from_changes_list(&changes_list), - files: changes_list, - }; -} - -fn produce_summary_from_changes_list( - changes_list: &Vec, -) -> ChangeAnalysisSummary { - let mut patch_totals = ChangePatchTotals { - hits: 0, - misses: 0, - partials: 0, - coverage: None, - }; - for fca in changes_list.iter() { - match &fca.added_diff_coverage { - None => {} - Some(cov_vec) => { - for (_, coverage) in cov_vec { - match coverage { - cov::Coverage::Hit => { - patch_totals.hits += 1; - } - cov::Coverage::Miss => { - patch_totals.misses += 1; - } - cov::Coverage::Partial(_) => { - patch_totals.partials += 1; - } - cov::Coverage::Ignore => {} - } - } - } - } - } - let loc = patch_totals.hits + patch_totals.misses + patch_totals.partials; - patch_totals.coverage = match loc { - 0 => None, - _ => Some(patch_totals.hits as f32 / loc as f32), - }; - ChangeAnalysisSummary { - patch_totals: patch_totals, - } -} - -fn run_filereport_analysis( - old_file: Option<&file::ReportFile>, - new_file: Option<&file::ReportFile>, - diff_data: Option<&diff::FileDiffData>, - names_tuple: (&str, &str), -) -> Option { - let (base_name, head_name) = names_tuple; - let is_new = match diff_data { - None => false, - Some(value) => value.0 == "new", - }; - let was_deleted = match diff_data { - None => false, - Some(value) => value.0 == "deleted", - }; - let (only_on_base, only_on_head) = diff::get_exclusions_from_diff(match diff_data { - None => None, - Some(x) => Some(&x.2), - }); - let removed_diff_coverage: Option> = match old_file { - None => None, - Some(file) => { - let mut a: Vec<(i32, cov::Coverage)> = Vec::new(); - for line in &only_on_base { - match file.lines.get(&line) { - None => {} - Some(l) => { - a.push((*line, l.coverage.to_owned())); - } - } - } - a.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); - Some(a) - } - }; - let added_diff_coverage: Option> = match new_file { - None => None, - Some(file) => { - let mut a: Vec<(i32, cov::Coverage)> = Vec::new(); - for line in &only_on_head { - match file.lines.get(&line) { - None => {} - Some(l) => { - a.push((*line, l.coverage.to_owned())); - } - } - } - a.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); - Some(a) - } - }; - let mut unexpected_vec = Vec::new(); - if !is_new && !was_deleted { - let mut current_base: i32 = 0; - let mut current_head: i32 = 0; - let base_eof = match old_file { - None => 0, - Some(base_report) => base_report.get_eof(), - }; - let head_eof = match new_file { - None => 0, - Some(head_report) => head_report.get_eof(), - }; - while current_base < base_eof || current_head < head_eof { - current_base += 1; - current_head += 1; - while only_on_base.contains(¤t_base) { - current_base += 1; - } - while only_on_head.contains(¤t_head) { - current_head += 1 - } - match ( - match old_file { - None => None, - Some(base_report) => base_report.lines.get(¤t_base), - }, - match new_file { - None => None, - Some(head_report) => head_report.lines.get(¤t_head), - }, - ) { - (None, None) => {} - (None, Some(head_line)) => { - unexpected_vec.push(( - (current_base, None), - (current_head, Some(head_line.coverage.to_owned())), - )); - } - (Some(base_line), None) => { - unexpected_vec.push(( - (current_base, Some(base_line.coverage.to_owned())), - (current_head, None), - )); - } - (Some(base_line), Some(head_line)) => { - if base_line.coverage != head_line.coverage { - unexpected_vec.push(( - (current_base, Some(base_line.coverage.to_owned())), - (current_head, Some(head_line.coverage.to_owned())), - )); - } - } - } - } - } - let mut lines_only_on_head: Vec<_> = only_on_head.iter().map(|k| *k).collect(); - lines_only_on_head.sort(); - let mut lines_only_on_base: Vec<_> = only_on_base.iter().map(|k| *k).collect(); - lines_only_on_base.sort(); - return match (old_file, new_file) { - (None, None) => None, - (None, Some(new)) => Some(FileChangesAnalysis { - file_was_added_by_diff: is_new, - file_was_removed_by_diff: was_deleted, - head_coverage: Some(new.get_totals()), - base_coverage: None, - removed_diff_coverage: removed_diff_coverage, - added_diff_coverage: added_diff_coverage, - unexpected_line_changes: unexpected_vec, - base_name: base_name.to_string(), - head_name: head_name.to_string(), - lines_only_on_head: lines_only_on_head, - lines_only_on_base: lines_only_on_base, - }), - (Some(old), None) => Some(FileChangesAnalysis { - file_was_added_by_diff: is_new, - file_was_removed_by_diff: was_deleted, - head_coverage: None, - base_coverage: Some(old.get_totals()), - removed_diff_coverage: removed_diff_coverage, - added_diff_coverage: added_diff_coverage, - unexpected_line_changes: unexpected_vec, - base_name: base_name.to_string(), - head_name: head_name.to_string(), - lines_only_on_head: lines_only_on_head, - lines_only_on_base: lines_only_on_base, - }), - (Some(base_report), Some(head_report)) => { - let has_removed_diff_coverage = match &removed_diff_coverage { - None => false, - Some(x) => !x.is_empty(), - }; - let has_added_diff_coverage = match &added_diff_coverage { - None => false, - Some(x) => !x.is_empty(), - }; - if unexpected_vec.is_empty() - && !has_removed_diff_coverage - && !has_added_diff_coverage - && lines_only_on_base.is_empty() - && lines_only_on_head.is_empty() - { - return None; - } - return Some(FileChangesAnalysis { - added_diff_coverage: added_diff_coverage, - base_coverage: Some(base_report.get_totals()), - base_name: base_name.to_string(), - file_was_added_by_diff: is_new, - file_was_removed_by_diff: was_deleted, - head_coverage: Some(head_report.get_totals()), - head_name: head_name.to_string(), - removed_diff_coverage: removed_diff_coverage, - unexpected_line_changes: unexpected_vec, - lines_only_on_head: lines_only_on_head, - lines_only_on_base: lines_only_on_base, - }); - } - }; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::line; - use fraction::GenericFraction; - - fn create_line_for_test(line_number: i32, coverage: cov::Coverage) -> (i32, line::ReportLine) { - ( - line_number, - line::ReportLine { - coverage: coverage.clone(), - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: coverage, - complexity: None, - }], - complexity: None, - }, - ) - } - - #[test] - fn get_changes_works() { - let first_report = report::Report { - report_files: vec![ - ( - "apple".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(1, cov::Coverage::Hit), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new( - 1, 2, - )), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "another_unmodified.py".to_string(), - file::ReportFile { - lines: vec![( - 22, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - )] - .into_iter() - .collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let second_report = report::Report { - report_files: vec![ - ( - "file1.go".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(1, cov::Coverage::Hit), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Miss, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 1, - coverage: cov::Coverage::Miss, - complexity: None, - }], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ( - "another_unmodified.py".to_string(), - file::ReportFile { - lines: vec![( - 22, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - )] - .into_iter() - .collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let diff: diff::DiffInput = vec![ - ( - ("file1.go").to_string(), - ( - "changed".to_string(), - Some("apple".to_string()), - vec![((1, 0, 1, 1), vec!["+".to_string()])], - ), - ), - ( - ("file_p.py").to_string(), - ( - "new".to_string(), - None, - vec![((1, 0, 1, 1), vec!["+".to_string()])], - ), - ), - ] - .into_iter() - .collect(); - let res = run_comparison_analysis(&first_report, &second_report, &diff); - let mut files_array: Vec<_> = res.files; - let present_keys: HashSet = - files_array.iter().map(|f| f.head_name.to_owned()).collect(); - assert!(present_keys.contains(&"file_p.py".to_string())); - assert!(present_keys.contains(&"file1.go".to_string())); - assert_eq!(present_keys.len(), 2); - files_array.sort_by(|a, b| a.base_name.partial_cmp(&b.base_name).unwrap()); - let one_case = files_array.get(0).unwrap(); - assert_eq!(one_case.base_name, "apple"); - assert_eq!(one_case.file_was_added_by_diff, false); - assert_eq!(one_case.file_was_removed_by_diff, false); - assert_eq!( - one_case.added_diff_coverage, - Some(vec![(1, cov::Coverage::Hit)]) - ); - assert_eq!(one_case.removed_diff_coverage, Some(vec![])); - assert_eq!(one_case.unexpected_line_changes.len(), 2); - } - - #[test] - fn last_line_near_end_case() { - let diff: diff::DiffInput = vec![( - "file1.go".to_string(), - ( - "modified".to_string(), - None, - vec![( - (988, 15, 988, 16), - vec![ - " ".to_string(), - " ".to_string(), - " ".to_string(), - "-".to_string(), - "-".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - " ".to_string(), - " ".to_string(), - " ".to_string(), - ], - )], - ), - )] - .into_iter() - .collect(); - let first_report = report::Report { - report_files: vec![( - "file1.go".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(1079, cov::Coverage::Hit), - create_line_for_test(1075, cov::Coverage::Miss), - ] - .into_iter() - .collect(), - }, - )] - .into_iter() - .collect(), - session_mapping: vec![(0, vec!["unit".to_string()])].into_iter().collect(), - }; - let second_report = report::Report { - report_files: vec![( - "file1.go".to_string(), - file::ReportFile { - lines: vec![create_line_for_test(1076, cov::Coverage::Miss)] - .into_iter() - .collect(), - }, - )] - .into_iter() - .collect(), - session_mapping: vec![(0, vec!["unit".to_string()])].into_iter().collect(), - }; - let full_res = run_comparison_analysis(&first_report, &second_report, &diff); - println!("{:?}", full_res); - assert_eq!( - full_res.changes_summary, - ChangeAnalysisSummary { - patch_totals: ChangePatchTotals { - hits: 0, - misses: 0, - partials: 0, - coverage: None - } - } - ); - assert_eq!(full_res.files.len(), 1); - let first_result_file = &full_res.files[0]; - assert_eq!(first_result_file.base_name, "file1.go"); - assert_eq!(first_result_file.head_name, "file1.go"); - assert_eq!(first_result_file.file_was_added_by_diff, false); - assert_eq!(first_result_file.file_was_removed_by_diff, false); - assert_eq!( - first_result_file.base_coverage, - Some(file::FileTotals { - hits: 1, - misses: 1, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0 - }) - ); - assert_eq!( - first_result_file.head_coverage, - Some(file::FileTotals { - hits: 0, - misses: 1, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0 - }) - ); - assert_eq!(first_result_file.removed_diff_coverage, Some(vec![])); - assert_eq!(first_result_file.added_diff_coverage, Some(vec![])); - assert_eq!( - first_result_file.unexpected_line_changes, - [((1079, Some(cov::Coverage::Hit)), (1080, None))] - ); - assert_eq!(first_result_file.lines_only_on_base, [991, 992]); - assert_eq!(first_result_file.lines_only_on_head, [998, 999, 1000]); - assert_eq!( - full_res.changes_summary, - ChangeAnalysisSummary { - patch_totals: ChangePatchTotals { - hits: 0, - misses: 0, - partials: 0, - coverage: None - } - } - ); - } - - #[test] - fn complete_case() { - let diff: diff::DiffInput = vec![ - ( - ("file_with_diff_only.md").to_string(), - ( - "changed".to_string(), - None, - vec![((1, 0, 1, 1), vec!["+".to_string()])], - ), - ), - ( - ("renamed_new.c").to_string(), - ( - "changed".to_string(), - Some("renamed_old.c".to_string()), - vec![((1, 0, 1, 1), vec!["+".to_string()])], - ), - ), - ( - ("renamed_new_with_changes.c").to_string(), - ( - "changed".to_string(), - Some("renamed_old_with_changes.c".to_string()), - vec![((100, 1, 100, 1), vec!["-".to_string(), "+".to_string()])], - ), - ), - ( - ("removed_file.c").to_string(), - ( - "deleted".to_string(), - None, - vec![( - (1, 4, 1, 0), - vec![ - "-".to_string(), - "-".to_string(), - "-".to_string(), - "-".to_string(), - ], - )], - ), - ), - ( - ("added_file.c").to_string(), - ( - "new".to_string(), - None, - vec![((1, 0, 1, 10), vec!["+".to_string(); 10])], - ), - ), - ( - ("file_with_unexpected_changes.c").to_string(), - ( - "changed".to_string(), - None, - vec![( - (21, 3, 21, 2), - vec![ - "-".to_string(), - "-".to_string(), - "-".to_string(), - "+".to_string(), - "+".to_string(), - ], - )], - ), - ), - ( - ("file_with_unexpec_and_cov_diff.c").to_string(), - ( - "changed".to_string(), - None, - vec![( - (65, 5, 65, 4), - vec![ - "-".to_string(), - "-".to_string(), - "-".to_string(), - "-".to_string(), - "-".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - ], - )], - ), - ), - ( - ("file_with_cov_remov_and_add.c").to_string(), - ( - "changed".to_string(), - None, - vec![ - ( - (5, 2, 5, 3), - vec![ - "-".to_string(), - "-".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - ], - ), - ( - (15, 3, 16, 10), - vec![ - "-".to_string(), - "-".to_string(), - " ".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - "+".to_string(), - ], - ), - ], - ), - ), - ] - .into_iter() - .collect(); - - let first_report = report::Report { - report_files: vec![ - ( - "unrelated_file.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(76, cov::Coverage::Hit), - create_line_for_test(79, cov::Coverage::Hit), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_cov_remov_and_add.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(6, cov::Coverage::Hit), - create_line_for_test(8, cov::Coverage::Hit), - create_line_for_test(16, cov::Coverage::Hit), - create_line_for_test(17, cov::Coverage::Hit), - create_line_for_test( - 18, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_unexpected_changes.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(5, cov::Coverage::Hit), - create_line_for_test(10, cov::Coverage::Miss), - create_line_for_test(15, cov::Coverage::Hit), - create_line_for_test(20, cov::Coverage::Hit), - create_line_for_test( - 25, - cov::Coverage::Partial(GenericFraction::new(1, 4)), - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_unexpec_and_cov_diff.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(64, cov::Coverage::Hit), - create_line_for_test(65, cov::Coverage::Hit), - create_line_for_test(66, cov::Coverage::Hit), - create_line_for_test(67, cov::Coverage::Miss), - create_line_for_test(68, cov::Coverage::Hit), - create_line_for_test(69, cov::Coverage::Hit), - create_line_for_test(70, cov::Coverage::Hit), - ] - .into_iter() - .collect(), - }, - ), - ( - "removed_file.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(1, cov::Coverage::Hit), - create_line_for_test(2, cov::Coverage::Miss), - create_line_for_test(3, cov::Coverage::Ignore), - create_line_for_test( - 4, - cov::Coverage::Partial(GenericFraction::new(1, 3)), - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "renamed_old.c".to_string(), - file::ReportFile { - lines: vec![create_line_for_test( - 101, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - )] - .into_iter() - .collect(), - }, - ), - ( - "renamed_old_with_changes.c".to_string(), - file::ReportFile { - lines: vec![create_line_for_test( - 101, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - )] - .into_iter() - .collect(), - }, - ), - ( - "missing.c".to_string(), - file::ReportFile { - lines: vec![create_line_for_test(2, cov::Coverage::Miss)] - .into_iter() - .collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![].into_iter().collect(), - }; - - let second_report = report::Report { - report_files: vec![ - ( - "unrelated_file.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(76, cov::Coverage::Hit), - create_line_for_test(79, cov::Coverage::Hit), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_cov_remov_and_add.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(6, cov::Coverage::Hit), - create_line_for_test(9, cov::Coverage::Hit), - create_line_for_test(16, cov::Coverage::Hit), - create_line_for_test(17, cov::Coverage::Hit), - create_line_for_test( - 26, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_unexpected_changes.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(5, cov::Coverage::Hit), - create_line_for_test(10, cov::Coverage::Miss), - create_line_for_test(15, cov::Coverage::Miss), - create_line_for_test(20, cov::Coverage::Hit), - create_line_for_test( - 25, - cov::Coverage::Partial(GenericFraction::new(1, 4)), - ), - ] - .into_iter() - .collect(), - }, - ), - ( - "file_with_unexpec_and_cov_diff.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test( - 64, - cov::Coverage::Partial(GenericFraction::new(1, 4)), - ), - create_line_for_test(65, cov::Coverage::Hit), - create_line_for_test(66, cov::Coverage::Hit), - create_line_for_test(67, cov::Coverage::Miss), - create_line_for_test(68, cov::Coverage::Hit), - create_line_for_test(69, cov::Coverage::Ignore), - ] - .into_iter() - .collect(), - }, - ), - ( - "added_file.c".to_string(), - file::ReportFile { - lines: vec![ - create_line_for_test(1, cov::Coverage::Miss), - create_line_for_test(4, cov::Coverage::Miss), - ] - .into_iter() - .collect(), - }, - ), - ( - "renamed_new.c".to_string(), - file::ReportFile { - lines: vec![create_line_for_test( - 102, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - )] - .into_iter() - .collect(), - }, - ), - ( - "renamed_new_with_changes.c".to_string(), - file::ReportFile { - lines: vec![create_line_for_test( - 102, - cov::Coverage::Partial(GenericFraction::new(1, 2)), - )] - .into_iter() - .collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![].into_iter().collect(), - }; - let full_res = run_comparison_analysis(&first_report, &second_report, &diff); - println!("{}", serde_json::to_string(&full_res).unwrap()); - assert_eq!( - full_res.changes_summary, - ChangeAnalysisSummary { - patch_totals: ChangePatchTotals { - hits: 5, - misses: 3, - partials: 0, - coverage: Some(0.625) - } - } - ); - let mut res = full_res.files; - // Sorting now will allow the below assertions to be deterministic - res.sort_by(|a, b| a.base_name.partial_cmp(&b.base_name).unwrap()); - assert_eq!( - vec![ - "added_file.c", - "file_with_cov_remov_and_add.c", - "file_with_unexpec_and_cov_diff.c", - "file_with_unexpected_changes.c", - "missing.c", - "removed_file.c", - "renamed_new.c", - "renamed_new_with_changes.c", - ], - res.iter() - .map(|b| b.head_name.to_string()) - .into_iter() - .collect::>() - ); - assert_eq!( - vec![ - "added_file.c", - "file_with_cov_remov_and_add.c", - "file_with_unexpec_and_cov_diff.c", - "file_with_unexpected_changes.c", - "missing.c", - "removed_file.c", - "renamed_old.c", - "renamed_old_with_changes.c" - ], - res.iter() - .map(|b| b.base_name.to_string()) - .into_iter() - .collect::>() - ); - assert_eq!( - vec![true, false, false, false, false, false, false, false], - res.iter() - .map(|b| b.file_was_added_by_diff) - .into_iter() - .collect::>() - ); - assert_eq!( - vec![false, false, false, false, false, true, false, false], - res.iter() - .map(|b| b.file_was_removed_by_diff) - .into_iter() - .collect::>() - ); - let results_mapping: HashMap = res - .into_iter() - .map(|a| (a.base_name.to_string(), a)) - .collect(); - let file_with_unexpec_and_cov_diff = results_mapping - .get("file_with_unexpec_and_cov_diff.c") - .expect("There should be a file_with_unexpec_and_cov_diff file here"); - assert_eq!( - file_with_unexpec_and_cov_diff.unexpected_line_changes, - vec![ - ( - (64, Some(cov::Coverage::Hit)), - (64, Some(cov::Coverage::Partial(GenericFraction::new(1, 4)))) - ), - ( - (70, Some(cov::Coverage::Hit)), - (69, Some(cov::Coverage::Ignore)) - ) - ] - ); - assert_eq!( - file_with_unexpec_and_cov_diff.removed_diff_coverage, - Some(vec![ - (65, cov::Coverage::Hit), - (66, cov::Coverage::Hit), - (67, cov::Coverage::Miss), - (68, cov::Coverage::Hit), - (69, cov::Coverage::Hit), - ]) - ); - assert_eq!( - file_with_unexpec_and_cov_diff.added_diff_coverage, - Some(vec![ - (65, cov::Coverage::Hit), - (66, cov::Coverage::Hit), - (67, cov::Coverage::Miss), - (68, cov::Coverage::Hit), - ]) - ); - } -} diff --git a/src/cov.rs b/src/cov.rs deleted file mode 100644 index bcd4118ee..000000000 --- a/src/cov.rs +++ /dev/null @@ -1,108 +0,0 @@ -use fraction::GenericFraction; -use fraction::ToPrimitive; -use serde::ser::{Serialize, Serializer}; - -#[derive(PartialEq, Debug, Clone)] -pub enum Coverage { - Hit, - Miss, - Partial(GenericFraction), - Ignore, -} - -impl Coverage { - fn as_char(&self) -> char { - return match self { - Coverage::Hit => 'h', - Coverage::Miss => 'm', - Coverage::Partial(_) => 'p', - Coverage::Ignore => 'i', - }; - } -} - -impl Serialize for Coverage { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_char(self.as_char()) - } -} - -impl Coverage { - pub fn get_value(&self) -> f64 { - match self { - Coverage::Hit => { - return 1.0; - } - Coverage::Miss => { - return 0.0; - } - Coverage::Partial(f) => { - return f.to_f64().unwrap(); - } - Coverage::Ignore => { - return -1.0; - } - } - } - - pub fn join_coverages(many_coverages: Vec<&Coverage>) -> Coverage { - let mut a: Coverage = Coverage::Ignore; - for cov in many_coverages.iter() { - match cov { - Coverage::Hit => return Coverage::Hit, - Coverage::Miss => { - if let Coverage::Ignore = a { - a = Coverage::Miss - } - } - Coverage::Partial(f) => { - if f.to_f64().unwrap() > a.get_value() { - a = Coverage::Partial(*f); - } - } - Coverage::Ignore => {} - } - } - return a; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn joining_coverage_works() { - let v = Coverage::join_coverages(vec![ - &Coverage::Miss, - &Coverage::Hit, - &Coverage::Partial(GenericFraction::new(3, 10)), - ]); - assert_eq!(v, Coverage::Hit); - let k = Coverage::join_coverages(vec![ - &Coverage::Miss, - &Coverage::Partial(GenericFraction::new(3, 10)), - ]); - assert_eq!(k, Coverage::Partial(GenericFraction::new(3, 10))); - assert_eq!(Coverage::join_coverages(vec![]), Coverage::Ignore); - assert_eq!( - Coverage::join_coverages(vec![&Coverage::Ignore, &Coverage::Miss]), - Coverage::Miss - ); - assert_eq!( - Coverage::join_coverages(vec![&Coverage::Ignore, &Coverage::Ignore]), - Coverage::Ignore - ); - assert_eq!( - Coverage::join_coverages(vec![&Coverage::Miss, &Coverage::Ignore]), - Coverage::Miss - ); - assert_eq!( - Coverage::join_coverages(vec![&Coverage::Miss, &Coverage::Ignore, &Coverage::Hit]), - Coverage::Hit - ); - } -} diff --git a/src/diff.rs b/src/diff.rs deleted file mode 100644 index 382b8af4c..000000000 --- a/src/diff.rs +++ /dev/null @@ -1,49 +0,0 @@ -use pyo3::prelude::*; -use std::collections::HashMap; -use std::collections::HashSet; - -use crate::file; - -#[pyclass] -#[derive(Debug)] -pub struct FileDiffAnalysis { - #[pyo3(get)] - pub summary: file::FileTotals, - pub lines_with_misses: Vec, - pub lines_with_partials: Vec, - pub lines_with_hits: Vec, -} - -type DiffSegment = ((i32, i32, i32, i32), Vec); - -pub type FileDiffData = (String, Option, Vec); - -pub type DiffInput = HashMap; - -pub fn get_exclusions_from_diff(diff: Option<&Vec>) -> (HashSet, HashSet) { - match diff { - None => return (HashSet::new(), HashSet::new()), - Some(val) => { - let mut only_on_base: HashSet = HashSet::new(); - let mut only_on_head: HashSet = HashSet::new(); - for (headers, line_list) in val { - let (start_base, _, start_head, _) = headers; - let mut current_base: i32 = *start_base; - let mut current_head: i32 = *start_head; - for individual_line in line_list { - if individual_line == "+" { - only_on_head.insert(current_head); - current_head += 1; - } else if individual_line == "-" { - only_on_base.insert(current_base); - current_base += 1; - } else { - current_head += 1; - current_base += 1; - } - } - } - return (only_on_base, only_on_head); - } - } -} diff --git a/src/file.rs b/src/file.rs deleted file mode 100644 index 6ee901dce..000000000 --- a/src/file.rs +++ /dev/null @@ -1,250 +0,0 @@ -use pyo3::prelude::*; -use serde::Serialize; -use std::collections::HashMap; - -use crate::cov; -use crate::line; - -#[pyclass] -#[derive(Debug, Clone, PartialEq, Serialize)] -pub struct FileTotals { - #[pyo3(get)] - pub hits: i32, - #[pyo3(get)] - pub misses: i32, - #[pyo3(get)] - pub partials: i32, - #[pyo3(get)] - pub branches: i32, - pub sessions: i32, - pub complexity: i32, - pub complexity_total: i32, - #[pyo3(get)] - pub methods: i32, -} - -#[pymethods] -impl FileTotals { - #[getter(lines)] - pub fn get_lines(&self) -> PyResult { - Ok(self.get_line_count()) - } -} - -impl FileTotals { - pub fn get_line_count(&self) -> i32 { - return self.hits + self.misses + self.partials; - } - - pub fn new() -> FileTotals { - FileTotals { - hits: 0, - misses: 0, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - } - } - - pub fn is_empty(&self) -> bool { - return self.get_line_count() == 0; - } - - pub fn from_lines(lines: Vec<&line::ReportLine>) -> FileTotals { - let mut res: FileTotals = FileTotals { - hits: 0, - misses: 0, - methods: 0, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - }; - for report_line in lines.iter() { - match report_line.coverage { - cov::Coverage::Hit => res.hits += 1, - cov::Coverage::Miss => res.misses += 1, - cov::Coverage::Partial(_) => res.partials += 1, - cov::Coverage::Ignore => {} - } - match &report_line.complexity { - Some(value) => match value { - line::Complexity::SingleComplexity(v) => { - res.complexity += v; - } - line::Complexity::TotalComplexity((n, d)) => { - res.complexity += n; - res.complexity_total += d; - } - }, - None => {} - } - match report_line.coverage_type { - line::CoverageType::Standard => {} - line::CoverageType::Branch => res.branches += 1, - line::CoverageType::Method => res.methods += 1, - } - } - return res; - } -} - -pub struct ReportFile { - pub lines: HashMap, -} - -impl ReportFile { - pub fn get_eof(&self) -> i32 { - return match self.lines.keys().max() { - Some(expr) => *expr + 1, - None => 0, - }; - } - - pub fn get_filtered_totals(&self, session_ids: &Vec) -> FileTotals { - let all_lines: Vec = self - .lines - .values() - .filter_map(|x| x.filter_by_session_ids(session_ids)) - .collect(); - return FileTotals::from_lines(all_lines.iter().collect()); - } - - pub fn calculate_per_flag_totals( - &self, - flag_mapping: &HashMap>, - ) -> HashMap { - let mut book_reviews: HashMap = HashMap::new(); - for (_, report_line) in self.lines.iter() { - for sess in &report_line.sessions { - match flag_mapping.get(&sess.id) { - Some(flags) => { - for f in flags { - let mut stat = - book_reviews.entry(f.to_string()).or_insert(FileTotals { - hits: 0, - misses: 0, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - }); - match sess.coverage { - cov::Coverage::Hit => stat.hits += 1, - cov::Coverage::Miss => stat.misses += 1, - cov::Coverage::Partial(_) => stat.partials += 1, - cov::Coverage::Ignore => {} - } - } - } - None => {} - } - } - } - return book_reviews; - } - - pub fn get_totals(&self) -> FileTotals { - return FileTotals::from_lines(self.lines.values().collect()); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use fraction::GenericFraction; - - fn create_line_for_test(line_number: i32, coverage: cov::Coverage) -> (i32, line::ReportLine) { - ( - line_number, - line::ReportLine { - coverage: coverage.clone(), - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: coverage, - complexity: None, - }], - complexity: None, - }, - ) - } - - #[test] - fn from_lines_empty() { - let expected_result = FileTotals { - hits: 0, - misses: 0, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - }; - let result = FileTotals::from_lines(vec![]); - assert_eq!(expected_result, result); - assert_eq!(result.get_line_count(), 0); - } - - #[test] - fn from_lines_some() { - let expected_result = FileTotals { - hits: 1, - misses: 1, - partials: 1, - branches: 1, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - }; - let result = FileTotals::from_lines(vec![ - &line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![], - complexity: None, - }, - &line::ReportLine { - coverage: cov::Coverage::Miss, - coverage_type: line::CoverageType::Branch, - sessions: vec![], - complexity: None, - }, - &line::ReportLine { - coverage: cov::Coverage::Partial(GenericFraction::new(3, 10)), - coverage_type: line::CoverageType::Standard, - sessions: vec![], - complexity: None, - }, - &line::ReportLine { - coverage: cov::Coverage::Ignore, - coverage_type: line::CoverageType::Standard, - sessions: vec![], - complexity: None, - }, - ]); - assert_eq!(expected_result, result); - assert_eq!(result.get_line_count(), 3); - } - - #[test] - fn get_eof_is_correct() { - let file = ReportFile { - lines: vec![ - create_line_for_test(101, cov::Coverage::Hit), - create_line_for_test(103, cov::Coverage::Miss), - ] - .into_iter() - .collect(), - }; - assert_eq!(file.get_eof(), 104); - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 42d53a3fb..000000000 --- a/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -use pyo3::exceptions::PyException; -use pyo3::prelude::*; -use pyo3::wrap_pyfunction; -use std::collections::HashMap; - -#[cfg(target_env = "musl")] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -mod analyzers; -mod changes; -mod cov; -mod diff; -mod file; -mod line; -mod parser; -mod profiling; -mod report; - -#[pyfunction] -fn parse_report( - filenames: HashMap, - chunks: &str, - session_mapping: HashMap>, -) -> PyResult { - let res = parser::parse_report_from_str(filenames, chunks, session_mapping); - match res { - Ok(val) => return Ok(val), - Err(_) => return Err(PyException::new_err("Unable to parse rust report")), - } -} - -#[pyfunction] -fn run_comparison_as_json( - base_report: &report::Report, - head_report: &report::Report, - diff: diff::DiffInput, -) -> PyResult { - return match serde_json::to_string(&changes::run_comparison_analysis( - base_report, - head_report, - &diff, - )) { - Ok(value) => Ok(value), - Err(_) => Err(PyException::new_err("Error serializing changes")), - }; -} - -/// A Python module implemented in Rust. -#[pymodule] -fn rustyribs(_py: Python, m: &PyModule) -> PyResult<()> { - m.add_function(wrap_pyfunction!(parse_report, m)?)?; - m.add_function(wrap_pyfunction!(run_comparison_as_json, m)?)?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - - Ok(()) -} diff --git a/src/line.rs b/src/line.rs deleted file mode 100644 index c7d15dd81..000000000 --- a/src/line.rs +++ /dev/null @@ -1,162 +0,0 @@ -use crate::cov; - -#[derive(Debug, Clone, PartialEq)] -pub enum CoverageType { - Standard, - Branch, - Method, -} - -#[derive(Debug, Clone)] -pub enum Complexity { - TotalComplexity((i32, i32)), - SingleComplexity(i32), -} - -#[derive(Debug, Clone)] -pub struct LineSession { - pub id: i32, - pub coverage: cov::Coverage, - pub complexity: Option, -} - -#[derive(Debug, Clone)] -pub struct ReportLine { - pub coverage: cov::Coverage, - pub coverage_type: CoverageType, - pub sessions: Vec, - pub complexity: Option, -} - -impl ReportLine { - pub fn filter_by_session_ids(&self, session_ids: &Vec) -> Option { - let valid_sessions: Vec = self - .sessions - .iter() - .filter(|k| session_ids.contains(&k.id)) - .map(|x| x.clone()) - .collect(); - if valid_sessions.is_empty() { - return None; - } - let coverage = self.calculate_sessions_coverage(&valid_sessions); - if let cov::Coverage::Ignore = coverage { - return None; - } - Some(ReportLine { - coverage: coverage, - coverage_type: self.coverage_type.clone(), - complexity: self.calculate_sessions_complexity(&valid_sessions), - sessions: valid_sessions, - }) - } - - pub fn calculate_sessions_coverage(&self, sessions: &Vec) -> cov::Coverage { - let valid_sessions: Vec<&cov::Coverage> = sessions.iter().map(|k| &k.coverage).collect(); - return cov::Coverage::join_coverages(valid_sessions); - } - - pub fn calculate_sessions_complexity(&self, sessions: &Vec) -> Option { - let complexities: Vec<(i32, i32)> = sessions - .iter() - .filter_map(|k| match &k.complexity { - Some(x) => match x { - Complexity::SingleComplexity(v) => Some((*v, 0)), - Complexity::TotalComplexity(v) => Some((v.0, v.1)), - }, - None => None, - }) - .collect(); - let complexity_total = complexities.iter().map(|x| x.1).max(); - match complexity_total { - None => return None, - Some(total) => { - let complexity = complexities.iter().map(|x| x.0).max().unwrap(); - if total > 0 { - return Some(Complexity::TotalComplexity((complexity, total))); - } - return Some(Complexity::SingleComplexity(complexity)); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use fraction::GenericFraction; - - #[test] - fn filter_by_session_ids_works() { - let a = ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: CoverageType::Standard, - sessions: vec![ - LineSession { - id: 0, - coverage: cov::Coverage::Miss, - complexity: None, - }, - LineSession { - id: 1, - coverage: cov::Coverage::Ignore, - complexity: None, - }, - LineSession { - id: 2, - coverage: cov::Coverage::Partial(GenericFraction::new(3, 10)), - complexity: None, - }, - LineSession { - id: 3, - coverage: cov::Coverage::Hit, - complexity: None, - }, - ], - complexity: None, - }; - let res_only_zero = a.filter_by_session_ids(&vec![0]).unwrap(); - assert_eq!(res_only_zero.coverage, cov::Coverage::Miss); - assert_eq!(res_only_zero.coverage_type, CoverageType::Standard); - assert_eq!(res_only_zero.sessions.len(), 1); - assert!(res_only_zero.complexity.is_none()); - assert!(a.filter_by_session_ids(&vec![1]).is_none()); - let res_only_two = a.filter_by_session_ids(&vec![2]).unwrap(); - assert_eq!( - res_only_two.coverage, - cov::Coverage::Partial(GenericFraction::new(3, 10)) - ); - assert_eq!(res_only_two.coverage_type, CoverageType::Standard); - assert_eq!(res_only_two.sessions.len(), 1); - assert!(res_only_two.complexity.is_none()); - let res_only_three = a.filter_by_session_ids(&vec![3]).unwrap(); - assert_eq!(res_only_three.coverage, cov::Coverage::Hit); - assert_eq!(res_only_three.coverage_type, CoverageType::Standard); - assert_eq!(res_only_three.sessions.len(), 1); - assert!(res_only_three.complexity.is_none()); - let res_zero_and_one = a.filter_by_session_ids(&vec![0, 1]).unwrap(); - assert_eq!(res_zero_and_one.coverage, cov::Coverage::Miss); - assert_eq!(res_zero_and_one.coverage_type, CoverageType::Standard); - assert_eq!(res_zero_and_one.sessions.len(), 2); - assert!(res_zero_and_one.complexity.is_none()); - let res_two_and_three = a.filter_by_session_ids(&vec![2, 3]).unwrap(); - assert_eq!(res_two_and_three.coverage, cov::Coverage::Hit); - assert_eq!(res_two_and_three.coverage_type, CoverageType::Standard); - assert_eq!(res_two_and_three.sessions.len(), 2); - assert!(res_two_and_three.complexity.is_none()); - let res_one_and_two_and_three_and_zero = - a.filter_by_session_ids(&vec![0, 1, 2, 3]).unwrap(); - assert_eq!( - res_one_and_two_and_three_and_zero.coverage, - cov::Coverage::Hit - ); - assert_eq!( - res_one_and_two_and_three_and_zero.coverage_type, - CoverageType::Standard - ); - assert_eq!(res_one_and_two_and_three_and_zero.sessions.len(), 4); - assert!(res_one_and_two_and_three_and_zero.complexity.is_none()); - assert!(a.filter_by_session_ids(&vec![5]).is_none()); - assert!(a.filter_by_session_ids(&vec![1, 5]).is_none()); - } -} diff --git a/src/parser.rs b/src/parser.rs deleted file mode 100644 index 71d0327c8..000000000 --- a/src/parser.rs +++ /dev/null @@ -1,576 +0,0 @@ -extern crate rayon; -use crate::cov; -use crate::file; -use crate::line; -use crate::report; - -use fraction::GenericFraction; -use rayon::prelude::*; - -use serde_json::Value; -use std::collections::HashMap; - -#[derive(Debug)] -enum LineType { - Content(line::ReportLine), - Emptyline, - Separator, - Details, - NoFile, -} - -#[derive(Debug)] -pub enum ParsingError { - UnexpectedValue, -} - -fn parse_coverage_type(val: &Value) -> Result { - match val { - Value::String(v) => { - if v == "m" { - return Ok(line::CoverageType::Method); - } - if v == "b" { - return Ok(line::CoverageType::Branch); - } - return Err(ParsingError::UnexpectedValue); - } - Value::Null => return Ok(line::CoverageType::Standard), - _ => { - return Err(ParsingError::UnexpectedValue); - } - } -} - -fn parse_coverage(line: &Value) -> Result { - match line { - Value::Number(o) => { - return match o.as_i64() { - Some(number) => { - if number > 0 { - Ok(cov::Coverage::Hit) - } else if number == -1 { - Ok(cov::Coverage::Ignore) - } else { - Ok(cov::Coverage::Miss) - } - } - None => match o.as_f64() { - Some(alternative_number) => { - if alternative_number > 0.0 { - Ok(cov::Coverage::Hit) - } else { - Ok(cov::Coverage::Miss) - } - } - None => Err(ParsingError::UnexpectedValue), - }, - }; - } - Value::String(s) => match s.rfind("/") { - Some(_) => { - let v: Vec<&str> = s.split('/').collect(); - let num: i32 = v[0].parse().unwrap(); - let den: i32 = v[1].parse().unwrap(); - if num == den { - return Ok(cov::Coverage::Hit); - } - if num == 0 { - return Ok(cov::Coverage::Miss); - } - let f: GenericFraction = GenericFraction::new(num, den); - return Ok(cov::Coverage::Partial(f)); - } - None => { - let val: i32 = s.parse().unwrap(); - return if val > 0 { - Ok(cov::Coverage::Hit) - } else { - Ok(cov::Coverage::Miss) - }; - } - }, - Value::Array(_) => { - return Err(ParsingError::UnexpectedValue); - } - Value::Null => return Ok(cov::Coverage::Ignore), - Value::Bool(b) => { - if *b { - return Ok(cov::Coverage::Partial(GenericFraction::new(1, 2))); - } - return Err(ParsingError::UnexpectedValue); - } - Value::Object(_) => { - return Err(ParsingError::UnexpectedValue); - } - } -} - -fn parse_complexity(val: &Value) -> Result, ParsingError> { - match val { - Value::Number(o) => { - return Ok(Some(line::Complexity::SingleComplexity( - o.as_i64().unwrap() as i32, - ))); - } - Value::String(_) => return Err(ParsingError::UnexpectedValue), - Value::Array(a) => { - return Ok(Some(line::Complexity::TotalComplexity(( - a[0].as_i64().ok_or(ParsingError::UnexpectedValue)? as i32, - a[1].as_i64().ok_or(ParsingError::UnexpectedValue)? as i32, - )))); - } - Value::Null => return Ok(None), - Value::Bool(_) => return Err(ParsingError::UnexpectedValue), - Value::Object(_) => return Err(ParsingError::UnexpectedValue), - } -} - -fn parse_line(line: &str) -> Result { - if line.is_empty() { - return Ok(LineType::Emptyline); - } - if line == "<<<<< end_of_chunk >>>>>" { - return Ok(LineType::Separator); - } - match serde_json::from_str(&line).unwrap() { - Value::Number(_) => return Err(ParsingError::UnexpectedValue), - Value::String(_) => return Err(ParsingError::UnexpectedValue), - Value::Array(array_data) => { - let mut sessions: Vec = Vec::new(); - if array_data.len() > 2 { - for el in array_data[2] - .as_array() - .ok_or(ParsingError::UnexpectedValue)? - { - let el_as_array = el.as_array().ok_or(ParsingError::UnexpectedValue)?; - sessions.push(line::LineSession { - id: el_as_array[0] - .as_i64() - .ok_or(ParsingError::UnexpectedValue)? - as i32, - coverage: parse_coverage(&el[1])?, - complexity: parse_complexity(if el_as_array.len() > 4 { - &el_as_array[4] - } else { - &Value::Null - })?, - }) - } - } - return Ok(LineType::Content(line::ReportLine { - coverage: parse_coverage(&array_data[0])?, - coverage_type: if array_data.len() > 1 { - parse_coverage_type(&array_data[1])? - } else { - line::CoverageType::Standard - }, - sessions: sessions, - complexity: parse_complexity(if array_data.len() > 4 { - &array_data[4] - } else { - &Value::Null - })?, - })); - } - Value::Null => return Ok(LineType::NoFile), - Value::Bool(_) => return Err(ParsingError::UnexpectedValue), - Value::Object(_) => return Ok(LineType::Details), - } -} - -pub fn parse_report_from_str( - filenames: HashMap, - chunks: &str, - session_mapping: HashMap>, -) -> Result { - let all_lines_with_errors: Vec<_> = chunks.par_lines().map(|line| parse_line(&line)).collect(); - let all_lines_or_errors: Result, _> = all_lines_with_errors.into_iter().collect(); - let all_lines = all_lines_or_errors?; - if all_lines.len() == 0 { - return Ok(report::Report { - report_files: HashMap::new(), - session_mapping: session_mapping, - }); - } - let mut current_report_lines: Option> = None; - let mut all_report_files: Vec> = Vec::new(); - let mut line_count = 1; - for line in all_lines { - match line { - LineType::Separator => { - all_report_files.push(match current_report_lines { - Some(lines) => Some(file::ReportFile { lines }), - None => None, - }); - current_report_lines = None; - line_count = 1; - } - LineType::Emptyline => { - line_count += 1; - } - LineType::Details => { - current_report_lines = Some(HashMap::new()); - } - LineType::NoFile => { - current_report_lines = None; - } - LineType::Content(report_line) => { - match current_report_lines.as_mut() { - Some(lines) => { - lines.insert(line_count, report_line); - } - None => {} - } - line_count += 1; - } - } - } - all_report_files.push(match current_report_lines { - Some(v) => Some(file::ReportFile { lines: v }), - None => None, - }); - let number_to_filename: HashMap = filenames - .iter() - .map(|(x, y)| (*y, x.to_string())) - .into_iter() - .collect(); - let filename_to_mapping: HashMap = all_report_files - .into_iter() - .enumerate() - .filter_map(|(current_count, value)| match value { - Some(file) => { - let filename = number_to_filename.get(&(current_count as i32)); - match filename { - Some(val) => Some((val.to_string(), file)), - None => None, - } - } - None => None, - }) - .into_iter() - .collect(); - return Ok(report::Report { - report_files: filename_to_mapping, - session_mapping: session_mapping, - }); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_adds_two() { - let content = "{} -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[0, null, [[0, 0], [1, 0]]] -<<<<< end_of_chunk >>>>> -{} -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 1]]] -[1, null, [[0, 1], [1, 1]]] -<<<<< end_of_chunk >>>>> -{} -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 1]]] - - -[1, null, [[0, 1], [1, 1]]] -[1, null, [[0, 0], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[0, null, [[0, 0], [1, 0]]] -"; - let filenames: HashMap = vec![ - ("file1.go".to_string(), 0), - ("file_two.go".to_string(), 1), - ("file_iii.go".to_string(), 2), - ] - .into_iter() - .collect(); - let mut flags: HashMap> = HashMap::new(); - flags.insert(1, ["flag_one".to_string()].to_vec()); - flags.insert( - 0, - ["flag_three".to_string(), "flag_two".to_string()].to_vec(), - ); - let res = parse_report_from_str(filenames, content, flags).expect("Unable to parse report"); - let calc = res.calculate_per_flag_totals(); - assert!(calc.contains_key("flag_one")); - let calc_2 = res.get_simple_totals().unwrap(); - assert_eq!(calc_2.get_coverage().unwrap(), Some("90.00000".to_string())); - } - - #[test] - fn parses_report_with_null_chunks() { - let content = "{} -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[0, null, [[0, 0], [1, 0]]] -<<<<< end_of_chunk >>>>> -null -<<<<< end_of_chunk >>>>> -{} -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 1]]] - - -[1, null, [[0, 1], [1, 1]]] -[1, null, [[0, 0], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] -[1, null, [[0, 1], [1, 0]]] - - -[1, null, [[0, 1], [1, 0]]] -[0, null, [[0, 0], [1, 0]]] -"; - let filenames: HashMap = vec![ - ("file1.go".to_string(), 0), - ("file_two.go".to_string(), 1), - ("file_iii.go".to_string(), 2), - ] - .into_iter() - .collect(); - let flags: HashMap> = vec![ - ( - 0, - ["flag_three".to_string(), "flag_two".to_string()].to_vec(), - ), - (1, vec!["flag_one".to_string()]), - ] - .into_iter() - .collect(); - let res = parse_report_from_str(filenames, content, flags).expect("Unable to parse report"); - let calc_2 = res.get_simple_totals().unwrap(); - assert_eq!(calc_2.get_coverage().unwrap(), Some("84.61538".to_string())); - assert_eq!(calc_2.files, 2); - assert_eq!(calc_2.hits, 11); - assert_eq!(calc_2.lines, 13); - let involved_filenames: Vec = - res.report_files.keys().map(|x| x.to_string()).collect(); - assert_eq!(involved_filenames.len(), 2); - assert_eq!( - res.report_files.get("file1.go").unwrap().get_totals().hits, - 2 - ); - assert_eq!( - res.report_files - .get("file1.go") - .unwrap() - .get_totals() - .misses, - 1 - ); - assert_eq!( - res.report_files - .get("file_iii.go") - .unwrap() - .get_totals() - .hits, - 9 - ); - assert_eq!( - res.report_files - .get("file_iii.go") - .unwrap() - .get_totals() - .misses, - 1 - ); - assert!(involved_filenames.contains(&"file1.go".to_string())); - assert!(involved_filenames.contains(&"file_iii.go".to_string())); - } - - #[test] - fn parses_empty_report() { - let filenames: HashMap = HashMap::new(); - let mut flags: HashMap> = HashMap::new(); - flags.insert(1, ["flag_one".to_string()].to_vec()); - flags.insert( - 0, - ["flag_three".to_string(), "flag_two".to_string()].to_vec(), - ); - let res = parse_report_from_str(filenames, "", flags).expect("Unable to parse report"); - assert_eq!(res.report_files.len(), 0); - let calc = res.calculate_per_flag_totals(); - assert!(calc.is_empty()); - let calc_2 = res.get_simple_totals().unwrap(); - assert_eq!(calc_2.get_coverage().unwrap(), None); - } - - #[test] - fn parse_line_simple_case() { - let res = parse_line("[1, null, [[0, 1], [1, 0]]]").expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!(l.coverage, cov::Coverage::Hit); - assert_eq!(l.coverage_type, line::CoverageType::Standard); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_line_huge_number_case() { - // This is just a line that doesn't fit in i64. Unfortunately, the JSON library only goes up to - // u64. We are just parsing as a float on those cases because f64 can handle knowing - // if a number if higher than zero just fine, and that's what python does anyway - let res = parse_line("[18446744073709551615, null, [[23, 18446744073709551615]]]") - .expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!(l.coverage, cov::Coverage::Hit); - assert_eq!(l.coverage_type, line::CoverageType::Standard); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_line_method_line() { - let res = parse_line("[1, \"m\", [[0, 1], [1, 1]]]").expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!(l.coverage, cov::Coverage::Hit); - assert_eq!(l.coverage_type, line::CoverageType::Method); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_line_unusual_zero_case() { - // Some zero - let res = - parse_line("[0.0, null, [[23, 18446744073709551615]]]").expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!(l.coverage, cov::Coverage::Miss); - assert_eq!(l.coverage_type, line::CoverageType::Standard); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_coverage_unusual_numbers() { - // Some zero - assert_eq!( - cov::Coverage::Miss, - parse_coverage(&serde_json::from_str("0.0").unwrap()) - .expect("Unable to parse coverage") - ); - assert_eq!( - cov::Coverage::Miss, - parse_coverage(&serde_json::from_str("0").unwrap()).expect("Unable to parse coverage") - ); - assert_eq!( - cov::Coverage::Ignore, - parse_coverage(&serde_json::from_str("-1").unwrap()).expect("Unable to parse coverage") - ); - assert_eq!( - cov::Coverage::Hit, - parse_coverage(&serde_json::from_str("0.5").unwrap()) - .expect("Unable to parse coverage") - ); - assert_eq!( - cov::Coverage::Hit, - parse_coverage(&serde_json::from_str("18446744073709551615").unwrap()) - .expect("Unable to parse coverage") - ); - // Slightly higher than u64 limit - assert_eq!( - cov::Coverage::Hit, - parse_coverage(&serde_json::from_str("18446744073709551699").unwrap()) - .expect("Unable to parse coverage") - ); - } - - #[test] - fn parse_line_boolean_case() { - let res = parse_line("[true, \"b\", [[0, true, null, null, null]]]") - .expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!( - l.coverage, - cov::Coverage::Partial(GenericFraction::new(1, 2)) - ); - assert_eq!(l.coverage_type, line::CoverageType::Branch); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_line_empty_lines() { - let res = parse_line("[null, \"b\", [[157, null]]]").expect("Unable to parse line"); - match res { - LineType::Content(l) => { - assert_eq!(l.coverage, cov::Coverage::Ignore); - assert_eq!(l.coverage_type, line::CoverageType::Branch); - } - _ => { - panic!("Bad res"); - } - } - } - - #[test] - fn parse_line_bad_line() { - let _res = parse_line("[1, \"b\", [[null, true, null, null, null]]]") - .expect_err("Line should have thrown an error"); - } - #[test] - fn parse_coverage_sample_different_fractions() { - let actual_partial = parse_coverage(&serde_json::from_str("\"1/2\"").unwrap()) - .expect("should have parsed correctly"); - assert_eq!( - actual_partial, - cov::Coverage::Partial(GenericFraction::new(1, 2)) - ); - let actual_hit = parse_coverage(&serde_json::from_str("\"3/3\"").unwrap()) - .expect("should have parsed correctly"); - assert_eq!(actual_hit, cov::Coverage::Hit); - let actual_miss = parse_coverage(&serde_json::from_str("\"0/4\"").unwrap()) - .expect("should have parsed correctly"); - assert_eq!(actual_miss, cov::Coverage::Miss); - } -} diff --git a/src/profiling.rs b/src/profiling.rs deleted file mode 100644 index dbaa5d23e..000000000 --- a/src/profiling.rs +++ /dev/null @@ -1,357 +0,0 @@ -use crate::changes; -use crate::diff; -use crate::report; -use pyo3::exceptions::PyException; -use pyo3::prelude::*; -use pyo3::types::PyType; - -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::collections::HashSet; - -#[derive(Debug, Serialize)] -struct GroupImpact { - files: Vec, - group_name: String, -} - -#[derive(Debug, Serialize)] -struct GroupFileImpact { - filename: String, - impacted_base_lines: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct SingleFileProfilingData { - filename: String, - ln_ex_ct: Vec<(i32, i32)>, -} - -#[derive(Serialize, Deserialize, Debug)] -struct SingleGroupProfilingData { - count: i32, - group_name: String, - files: Vec, -} - -impl SingleGroupProfilingData { - fn find_impacted_endpoints( - &self, - files: &Vec, - ) -> Option { - let inside_group_file_mapping: HashMap = self - .files - .iter() - .map(|k| (k.filename.to_owned(), k)) - .collect(); - let files_impacts: Vec = files - .iter() - .map(|file_change_data| { - match inside_group_file_mapping.get(&file_change_data.base_name) { - Some(file_profiling_data) => { - let profiling_lines: HashSet = file_profiling_data - .ln_ex_ct - .iter() - .map(|(ln, _)| *ln) - .collect(); - let changed_lines: HashSet = - match &file_change_data.removed_diff_coverage { - Some(removed_lines) => { - removed_lines.iter().map(|(ln, _)| *ln).collect() - } - None => HashSet::new(), - }; - let impacted_lines: Vec = profiling_lines - .intersection(&changed_lines) - .map(|x| *x) - .collect(); - if !impacted_lines.is_empty() { - Some(GroupFileImpact { - filename: file_change_data.base_name.to_owned(), - impacted_base_lines: impacted_lines, - }) - } else { - None - } - } - None => None, - } - }) - .filter_map(|x| x) - .collect(); - if files_impacts.is_empty() { - return None; - } - return Some(GroupImpact { - group_name: self.group_name.to_owned(), - files: files_impacts, - }); - } -} - -#[pyclass] -pub struct ProfilingData { - groups: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ProfilingDataJson { - files: Vec, - groups: Vec, -} - -#[pymethods] -impl ProfilingData { - #[classmethod] - pub fn load_from_json(_cls: &PyType, json_str: &str) -> PyResult { - let json_data: Result = serde_json::from_str(json_str); - match json_data { - Ok(result) => { - return Ok(ProfilingData { - groups: result.groups, - }) - } - Err(_) => Err(PyException::new_err("Error loading full profiling data")), - } - } - - fn find_impacted_endpoints_json( - &self, - base_report: &report::Report, - head_report: &report::Report, - diff: diff::DiffInput, - ) -> PyResult { - let res = self.find_impacted_endpoints(base_report, head_report, diff); - return match serde_json::to_string(&res) { - Ok(value) => Ok(value), - Err(_) => Err(PyException::new_err("Error serializing impact")), - }; - } - - fn apply_diff_changes(&mut self, _diff: diff::DiffInput) {} -} - -impl ProfilingData { - fn find_impacted_endpoints( - &self, - base_report: &report::Report, - head_report: &report::Report, - diff: diff::DiffInput, - ) -> Vec { - let k = changes::run_comparison_analysis(base_report, head_report, &diff); - return self - .groups - .iter() - .map(|group| group.find_impacted_endpoints(&k.files)) - .filter_map(|x| x) - .collect(); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cov; - use crate::file; - use crate::line; - use fraction::GenericFraction; - use std::fs::File; - use std::io::Read; - - #[test] - fn it_parses_data() { - let mut contents = String::new(); - let mut file = - File::open("tests/samples/sample_opentelem_collected.json").expect("file not there"); - file.read_to_string(&mut contents); - let v: ProfilingDataJson = serde_json::from_str(&contents).expect("Not a valid json stuff"); - let filenames: Vec = v.files.iter().map(|f| f.filename.to_owned()).collect(); - assert_eq!( - filenames, - vec![ - "helpers/logging_config.py", - "services/redis.py", - "tasks/base.py", - "tasks/upload.py", - "database/base.py", - "database/engine.py", - "database/models/core.py", - "database/models/reports.py", - "helpers/cache.py", - "helpers/pathmap/pathmap.py", - "helpers/pathmap/tree.py", - "services/archive.py", - "services/bots.py", - "services/repository.py", - "services/storage.py", - "services/path_fixer/__init__.py", - "services/path_fixer/fixpaths.py", - "services/path_fixer/user_path_fixes.py", - "services/path_fixer/user_path_includes.py", - "services/report/__init__.py", - "services/report/parser.py", - "services/report/raw_upload_processor.py", - "services/report/report_processor.py", - "services/report/languages/base.py", - "services/report/languages/clover.py", - "services/report/languages/cobertura.py", - "services/report/languages/csharp.py", - "services/report/languages/helpers.py", - "services/report/languages/jacoco.py", - "services/report/languages/jetbrainsxml.py", - "services/report/languages/mono.py", - "services/report/languages/scoverage.py", - "services/report/languages/vb.py", - "services/report/languages/vb2.py", - "services/yaml/reader.py", - "tasks/upload_processor.py" - ] - ); - } - - #[test] - fn it_calculates_impacted_endpoints_correctly() { - let v: ProfilingData = ProfilingData { - groups: vec![SingleGroupProfilingData { - count: 10, - group_name: "GET /data".to_string(), - files: vec![SingleFileProfilingData { - filename: "file1.go".to_string(), - ln_ex_ct: vec![(1, 10), (2, 8)], - }], - }], - }; - let first_file_head = file::ReportFile { - lines: vec![ - ( - 1, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }], - complexity: None, - }, - ), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new(1, 2)), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }; - let first_file_base = file::ReportFile { - lines: vec![ - ( - 1, - line::ReportLine { - coverage: cov::Coverage::Miss, - coverage_type: line::CoverageType::Standard, - sessions: vec![line::LineSession { - id: 0, - coverage: cov::Coverage::Miss, - complexity: None, - }], - complexity: None, - }, - ), - ( - 2, - line::ReportLine { - coverage: cov::Coverage::Hit, - coverage_type: line::CoverageType::Standard, - sessions: vec![ - line::LineSession { - id: 0, - coverage: cov::Coverage::Hit, - complexity: None, - }, - line::LineSession { - id: 1, - coverage: cov::Coverage::Partial(GenericFraction::new(1, 2)), - complexity: None, - }, - ], - complexity: None, - }, - ), - ] - .into_iter() - .collect(), - }; - let head_report = report::Report { - report_files: vec![ - ("file1.go".to_string(), first_file_head), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let base_report = report::Report { - report_files: vec![ - ("file1.go".to_string(), first_file_base), - ( - "file_p.py".to_string(), - file::ReportFile { - lines: vec![].into_iter().collect(), - }, - ), - ] - .into_iter() - .collect(), - session_mapping: vec![ - (0, vec!["unit".to_string()]), - (1, vec!["integration".to_string()]), - ] - .into_iter() - .collect(), - }; - let diffinput: diff::DiffInput = vec![( - "file1.go".to_string(), - ( - "changed".to_string(), - None, - vec![((1, 1, 1, 1), vec!["-".to_string(), "+".to_string()])], - ), - )] - .into_iter() - .collect(); - let res = v.find_impacted_endpoints(&base_report, &head_report, diffinput); - assert_eq!(res.len(), 1); - assert_eq!(res[0].group_name, "GET /data"); - assert_eq!(res[0].files.len(), 1); - assert_eq!(res[0].files[0].filename, "file1.go".to_string()); - assert_eq!(res[0].files[0].impacted_base_lines, vec![1]); - } -} diff --git a/src/report.rs b/src/report.rs deleted file mode 100644 index bea7bdd65..000000000 --- a/src/report.rs +++ /dev/null @@ -1,237 +0,0 @@ -use crate::file; - -use pyo3::prelude::*; -use pyo3::types::PyDict; -use rayon::prelude::*; - -use fraction::GenericFraction; -use fraction::ToPrimitive; -use std::collections::HashMap; - -#[pyclass] -#[derive(Debug)] -pub struct ReportTotals { - #[pyo3(get)] - pub files: i32, - #[pyo3(get)] - pub lines: i32, - #[pyo3(get)] - pub hits: i32, - #[pyo3(get)] - pub misses: i32, - #[pyo3(get)] - pub partials: i32, - #[pyo3(get)] - pub branches: i32, - #[pyo3(get)] - pub sessions: i32, - pub complexity: i32, - #[pyo3(get)] - pub complexity_total: i32, - #[pyo3(get)] - pub methods: i32, -} - -impl ReportTotals { - pub fn new() -> ReportTotals { - ReportTotals { - files: 0, - lines: 0, - hits: 0, - misses: 0, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - } - } -} - -impl ReportTotals { - pub fn add_up(&mut self, other: &file::FileTotals) { - if !other.is_empty() { - self.files += 1; - self.lines += other.get_line_count(); - self.hits += other.hits; - self.misses += other.misses; - self.partials += other.partials; - self.branches += other.branches; - self.complexity += other.complexity; - self.complexity_total += other.complexity_total; - self.methods += other.methods; - } - } -} - -#[pymethods] -impl ReportTotals { - #[getter(coverage)] - pub fn get_coverage(&self) -> PyResult> { - if self.lines == 0 { - return Ok(None); - } - if self.hits == self.lines { - return Ok(Some("100".to_string())); - } - if self.hits == 0 { - return Ok(Some("0".to_string())); - } - let fraction: GenericFraction = GenericFraction::new(100 * self.hits, self.lines); - Ok(Some(format!("{:#.5}", fraction.to_f64().unwrap()))) - } - - #[getter(complexity)] - fn get_complexity(&self) -> PyResult> { - if self.lines == 0 { - return Ok(None); - } - return Ok(Some(self.complexity)); - } - - fn asdict<'a>(&self, py: Python<'a>) -> &'a PyDict { - let t = PyDict::new(py); - t.set_item("files", self.files).unwrap(); - t.set_item("lines", self.lines).unwrap(); - t.set_item("hits", self.hits).unwrap(); - t.set_item("misses", self.misses).unwrap(); - t.set_item("partials", self.partials).unwrap(); - t.set_item("branches", self.branches).unwrap(); - t.set_item("sessions", self.sessions).unwrap(); - t.set_item("complexity", self.complexity).unwrap(); - t.set_item("complexity_total", self.complexity_total) - .unwrap(); - t.set_item("methods", self.methods).unwrap(); - t.set_item("coverage", self.get_coverage().unwrap()) - .unwrap(); - t.set_item("diff", 0).unwrap(); - t.set_item("messages", 0).unwrap(); - return t; - } - - fn to_str(&self) -> String { - format!("{:?}", self) - } - - fn __str__(&self) -> String { - format!("{:?}", self) - } -} - -#[pyclass] -pub struct Report { - pub report_files: HashMap, - pub session_mapping: HashMap>, -} - -impl Report { - pub fn get_sessions_from_flags(&self, flags: &Vec) -> Vec { - let mut res: Vec = Vec::new(); - for (session_id, session_flags) in self.session_mapping.iter() { - if session_flags.iter().any(|v| flags.contains(&v.to_string())) { - res.push(*session_id); // TODO Do I have to use this * ? - } - } - return res; - } - - pub fn get_by_filename(&self, filename: &str) -> Option<&file::ReportFile> { - match self.report_files.get(filename) { - Some(file_report) => return Some(file_report), - None => { - return None; - } - } - } -} - -impl Report { - pub fn calculate_per_flag_totals(&self) -> HashMap { - let mut book_reviews: HashMap = HashMap::new(); - for (_, report_file) in self.report_files.iter() { - let file_totals = report_file.calculate_per_flag_totals(&self.session_mapping); - for (flag_name, totals) in file_totals.iter() { - let current = book_reviews - .entry(flag_name.to_string()) - .or_insert(ReportTotals::new()); - current.add_up(&totals) - } - } - return book_reviews; - } - - pub fn get_simple_totals(&self) -> PyResult { - let mut res = ReportTotals::new(); - let individual_totals: Vec = self - .report_files - .par_iter() - .map(|(_, x)| x.get_totals()) - .collect(); - for totals in individual_totals { - res.add_up(&totals); - } - res.sessions = self.session_mapping.len() as i32; - return Ok(res); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn adds_up_right() { - let mut totals = ReportTotals { - files: 0, - lines: 2, - hits: 2, - misses: 0, - partials: 0, - branches: 9, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 2, - }; - let f = file::FileTotals { - hits: 2, - misses: 3, - partials: 5, - branches: 7, - sessions: 12, - complexity: 19, - complexity_total: 31, - methods: 50, - }; - totals.add_up(&f); - assert_eq!(totals.files, 1); - assert_eq!(totals.lines, 12); - assert_eq!(totals.hits, 4); - assert_eq!(totals.misses, 3); - assert_eq!(totals.partials, 5); - assert_eq!(totals.branches, 16); - assert_eq!(totals.sessions, 0); - assert_eq!(totals.complexity, 19); - assert_eq!(totals.complexity_total, 31); - assert_eq!(totals.methods, 52); - assert_eq!(totals.get_coverage().unwrap(), Some("33.33333".to_string())); - } - - #[test] - fn rounds_right() { - let t = ReportTotals { - files: 2, - lines: 355, - hits: 261, - misses: 94, - partials: 0, - branches: 0, - sessions: 0, - complexity: 0, - complexity_total: 0, - methods: 0, - }; - assert_eq!(t.get_coverage().unwrap(), Some("73.52113".to_string())); - } -} diff --git a/tests/unit/reports/test_changes.py b/tests/unit/reports/test_changes.py index f946359e9..5262fdb5b 100644 --- a/tests/unit/reports/test_changes.py +++ b/tests/unit/reports/test_changes.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest +from cc_rustyribs import rustify_diff from shared.reports.changes import ( _get_changes_from_comparison, @@ -9,7 +10,6 @@ ) from shared.reports.readonly import ReadOnlyReport from shared.reports.types import Change -from shared.ribs import rustify_diff current_file = Path(__file__) diff --git a/tests/unit/test_profiling.py b/tests/unit/test_profiling.py index 66addd239..43a4f4252 100644 --- a/tests/unit/test_profiling.py +++ b/tests/unit/test_profiling.py @@ -2,6 +2,7 @@ from pathlib import Path import pytest +from cc_rustyribs import rustify_diff from shared.profiling import ( ProfilingDataFullAnalyzer, @@ -16,7 +17,6 @@ ReportLine, Session, ) -from shared.ribs import rustify_diff here = Path(__file__)