From 4f3904d61eab0b3e14d7aa8acf59d191b3815c94 Mon Sep 17 00:00:00 2001 From: Michael Kerscher Date: Tue, 11 Feb 2025 12:02:47 +0100 Subject: [PATCH 1/4] tests: evaluate slide sizes of a given list of html files. This will render mdbook-slide-evaluator useless and it will be deprecated soon --- tests/src/recreate-slide.list.sh | 17 ++ tests/src/slide-size.test.ts | 20 ++ tests/src/slides/slides.list.ts | 332 +++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100755 tests/src/recreate-slide.list.sh create mode 100644 tests/src/slide-size.test.ts create mode 100644 tests/src/slides/slides.list.ts diff --git a/tests/src/recreate-slide.list.sh b/tests/src/recreate-slide.list.sh new file mode 100755 index 000000000000..23fa00bc922a --- /dev/null +++ b/tests/src/recreate-slide.list.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e +BASEDIR="$(dirname "$0")" + +pushd "${BASEDIR}/../../book/html/" +# exclude redirect pages, solutions and exercise +SLIDES=$(grep -L "Redirecting to..." -R --include "*.html" --exclude "exercise.html" --exclude "solution.html") +popd +OUTPUT="${BASEDIR}/slides/slides.list.ts" + +# create a ts module that can be imported in the tests +echo "export const slides = [" > ${OUTPUT}; +for SLIDE in ${SLIDES}; do +echo " \"${SLIDE}\"," >> ${OUTPUT}; +done; +echo "];" >> ${OUTPUT}; diff --git a/tests/src/slide-size.test.ts b/tests/src/slide-size.test.ts new file mode 100644 index 000000000000..4b92cb910afc --- /dev/null +++ b/tests/src/slide-size.test.ts @@ -0,0 +1,20 @@ +import { describe, it } from "mocha"; +import { $, expect, browser } from "@wdio/globals"; +import { slides } from "./slides/slides.list"; + +describe("Slide", () => { + for (const slide of slides) { + it( + " " + + slide + + " should not be higher than 1333 pixels or wider than 750 pixels", + async () => { + await browser.url("/" + slide); + const main_element = $("#content > main"); + const main_element_size = await main_element.getSize(); + expect(main_element_size.height < 1333).toBe(true); + expect(main_element_size.width <= 750).toBe(true); + }, + ); + } +}); diff --git a/tests/src/slides/slides.list.ts b/tests/src/slides/slides.list.ts new file mode 100644 index 000000000000..72c4b34a3c53 --- /dev/null +++ b/tests/src/slides/slides.list.ts @@ -0,0 +1,332 @@ +export const slides = [ + "chromium.html", + "android/aidl.html", + "android/aidl/example-service/deploy.html", + "android/aidl/example-service/client.html", + "android/aidl/example-service/changing-implementation.html", + "android/aidl/example-service/changing-definition.html", + "android/aidl/example-service/interface.html", + "android/aidl/example-service/server.html", + "android/aidl/example-service/service.html", + "android/aidl/example-service/service-bindings.html", + "android/aidl/birthday-service.html", + "android/aidl/types/parcelables.html", + "android/aidl/types/arrays.html", + "android/aidl/types/primitives.html", + "android/aidl/types/objects.html", + "android/aidl/types/file-descriptor.html", + "android/aidl/types.html", + "android/interoperability/cpp.html", + "android/interoperability/with-c.html", + "android/interoperability/cpp/cpp-exception.html", + "android/interoperability/cpp/rust-result.html", + "android/interoperability/cpp/type-mapping.html", + "android/interoperability/cpp/bridge.html", + "android/interoperability/cpp/android-build-cpp.html", + "android/interoperability/cpp/cpp-bridge.html", + "android/interoperability/cpp/rust-bridge.html", + "android/interoperability/cpp/android-cpp-genrules.html", + "android/interoperability/cpp/shared-types.html", + "android/interoperability/cpp/generated-cpp.html", + "android/interoperability/cpp/android-build-rust.html", + "android/interoperability/cpp/shared-enums.html", + "android/interoperability/with-c/bindgen.html", + "android/interoperability/with-c/rust.html", + "android/interoperability/with-c/rust-library.html", + "android/interoperability/with-c/run-our-binary.html", + "android/interoperability/with-c/c-library.html", + "android/interoperability/java.html", + "android/testing/mocking.html", + "android/testing/googletest.html", + "android/interoperability.html", + "android/build-rules/binary.html", + "android/build-rules/library.html", + "android/testing.html", + "android/setup.html", + "android/build-rules.html", + "android/logging.html", + "references/shared.html", + "references/exclusive.html", + "references/slices.html", + "references/dangling.html", + "references/strings.html", + "hello-world/what-is-rust.html", + "hello-world/benefits.html", + "hello-world/playground.html", + "borrowing.html", + "memory-management/clone.html", + "memory-management/drop.html", + "memory-management/ownership.html", + "memory-management/copy-types.html", + "memory-management/review.html", + "memory-management/approaches.html", + "memory-management/move.html", + "welcome-day-2-afternoon.html", + "toc.html", + "welcome-day-3-afternoon.html", + "index.html", + "other-resources.html", + "running-the-course/keyboard-shortcuts.html", + "running-the-course/course-structure.html", + "running-the-course/translations.html", + "iterators.html", + "welcome-day-1-afternoon.html", + "generics.html", + "hello-world.html", + "welcome-day-3.html", + "bare-metal/android/vmbase.html", + "bare-metal/alloc.html", + "bare-metal/useful-crates.html", + "bare-metal/aps/mmio.html", + "bare-metal/aps/entry-point.html", + "bare-metal/aps/uart.html", + "bare-metal/aps/inline-assembly.html", + "bare-metal/aps/better-uart.html", + "bare-metal/aps/other-projects.html", + "bare-metal/aps/exceptions.html", + "bare-metal/aps/better-uart/using.html", + "bare-metal/aps/better-uart/bitflags.html", + "bare-metal/aps/better-uart/registers.html", + "bare-metal/aps/better-uart/driver.html", + "bare-metal/aps/logging/using.html", + "bare-metal/aps/logging.html", + "bare-metal/aps/uart/traits.html", + "bare-metal/aps/uart/using.html", + "bare-metal/aps.html", + "bare-metal/no_std.html", + "bare-metal/useful-crates/buddy_system_allocator.html", + "bare-metal/useful-crates/zerocopy.html", + "bare-metal/useful-crates/spin.html", + "bare-metal/useful-crates/aarch64-paging.html", + "bare-metal/useful-crates/tinyvec.html", + "bare-metal/android.html", + "bare-metal/microcontrollers.html", + "bare-metal/microcontrollers/mmio.html", + "bare-metal/microcontrollers/embedded-hal.html", + "bare-metal/microcontrollers/debugging.html", + "bare-metal/microcontrollers/pacs.html", + "bare-metal/microcontrollers/probe-rs.html", + "bare-metal/microcontrollers/other-projects.html", + "bare-metal/microcontrollers/board-support.html", + "bare-metal/microcontrollers/hals.html", + "bare-metal/microcontrollers/type-state.html", + "bare-metal/minimal.html", + "std-types/result.html", + "std-types/std.html", + "std-types/vec.html", + "std-types/string.html", + "std-types/docs.html", + "std-types/hashmap.html", + "std-types/option.html", + "pattern-matching/infallible.html", + "pattern-matching/let-control-flow/let-else.html", + "pattern-matching/let-control-flow/while-let.html", + "pattern-matching/let-control-flow/if-let.html", + "pattern-matching/destructuring-structs.html", + "pattern-matching/destructuring-enums.html", + "pattern-matching/let-control-flow.html", + "pattern-matching/match.html", + "closures/traits.html", + "closures/capturing.html", + "closures/syntax.html", + "print.html", + "cargo.html", + "borrowing/shared.html", + "borrowing/interior-mutability.html", + "borrowing/interior-mutability/cell.html", + "borrowing/interior-mutability/refcell.html", + "borrowing/examples.html", + "borrowing/borrowck.html", + "smart-pointers.html", + "methods-and-traits.html", + "testing/other.html", + "testing/lints.html", + "testing/unit-tests.html", + "tuples-and-arrays/tuples.html", + "tuples-and-arrays/destructuring.html", + "tuples-and-arrays/iteration.html", + "tuples-and-arrays/arrays.html", + "iterators/collect.html", + "iterators/motivation.html", + "iterators/intoiterator.html", + "iterators/helpers.html", + "iterators/iterator.html", + "credits.html", + "types-and-values.html", + "unsafe-rust/unsafe-functions/rust.html", + "unsafe-rust/unsafe-functions/extern-c.html", + "unsafe-rust/unsafe-functions/calling.html", + "unsafe-rust/unsafe-traits.html", + "unsafe-rust/unions.html", + "unsafe-rust/mutable-static.html", + "unsafe-rust/unsafe.html", + "unsafe-rust/dereferencing.html", + "unsafe-rust/unsafe-functions.html", + "closures.html", + "modules/filesystem.html", + "modules/visibility.html", + "modules/paths.html", + "modules/modules.html", + "modules/encapsulation.html", + "generics/generic-functions.html", + "generics/generic-data.html", + "generics/impl-trait.html", + "generics/dyn-trait.html", + "generics/trait-bounds.html", + "generics/generic-traits.html", + "unsafe-rust.html", + "lifetimes.html", + "smart-pointers/rc.html", + "smart-pointers/box.html", + "smart-pointers/trait-objects.html", + "exercises/bare-metal/afternoon.html", + "exercises/bare-metal/compass.html", + "exercises/bare-metal/morning.html", + "exercises/bare-metal/solutions-afternoon.html", + "exercises/bare-metal/rtc.html", + "exercises/bare-metal/solutions-morning.html", + "exercises/chromium/interoperability-with-cpp.html", + "exercises/chromium/bringing-it-together.html", + "exercises/chromium/solutions.html", + "exercises/chromium/third-party.html", + "exercises/chromium/testing.html", + "exercises/chromium/build-rules.html", + "error-handling.html", + "cargo/running-locally.html", + "cargo/rust-ecosystem.html", + "cargo/code-samples.html", + "types-and-values/inference.html", + "types-and-values/hello-world.html", + "types-and-values/variables.html", + "types-and-values/values.html", + "types-and-values/arithmetic.html", + "concurrency/async-pitfalls.html", + "concurrency/welcome-async.html", + "concurrency/async-exercises/chat-app.html", + "concurrency/async-exercises/dining-philosophers.html", + "concurrency/async-exercises/solutions.html", + "concurrency/send-sync.html", + "concurrency/channels.html", + "concurrency/async-exercises.html", + "concurrency/sync-exercises/dining-philosophers.html", + "concurrency/sync-exercises/solutions.html", + "concurrency/sync-exercises/link-checker.html", + "concurrency/threads/plain.html", + "concurrency/threads/scoped.html", + "concurrency/async-control-flow.html", + "concurrency/async-pitfalls/async-traits.html", + "concurrency/async-pitfalls/cancellation.html", + "concurrency/async-pitfalls/pin.html", + "concurrency/async-pitfalls/blocking-executor.html", + "concurrency/channels/unbounded.html", + "concurrency/channels/bounded.html", + "concurrency/channels/senders-receivers.html", + "concurrency/shared-state.html", + "concurrency/welcome.html", + "concurrency/sync-exercises.html", + "concurrency/async.html", + "concurrency/shared-state/arc.html", + "concurrency/shared-state/example.html", + "concurrency/shared-state/mutex.html", + "concurrency/send-sync/sync.html", + "concurrency/send-sync/send.html", + "concurrency/send-sync/marker-traits.html", + "concurrency/send-sync/examples.html", + "concurrency/threads.html", + "concurrency/async-control-flow/channels.html", + "concurrency/async-control-flow/select.html", + "concurrency/async-control-flow/join.html", + "concurrency/async/futures.html", + "concurrency/async/tasks.html", + "concurrency/async/async-await.html", + "concurrency/async/runtimes.html", + "concurrency/async/runtimes/tokio.html", + "methods-and-traits/traits.html", + "methods-and-traits/traits/associated-types.html", + "methods-and-traits/traits/supertraits.html", + "methods-and-traits/traits/implementing.html", + "methods-and-traits/deriving.html", + "methods-and-traits/methods.html", + "memory-management.html", + "lifetimes/struct-lifetimes.html", + "lifetimes/lifetime-annotations.html", + "lifetimes/lifetime-elision.html", + "chromium/policy.html", + "chromium/interoperability-with-cpp.html", + "chromium/cargo.html", + "chromium/testing/rust-gtest-interop.html", + "chromium/testing/chromium-import-macro.html", + "chromium/testing/build-gn.html", + "chromium/adding-third-party-crates.html", + "chromium/interoperability-with-cpp/error-handling-png.html", + "chromium/interoperability-with-cpp/example-bindings.html", + "chromium/interoperability-with-cpp/using-cxx-in-chromium.html", + "chromium/interoperability-with-cpp/error-handling.html", + "chromium/interoperability-with-cpp/limitations-of-cxx.html", + "chromium/interoperability-with-cpp/error-handling-qr.html", + "chromium/adding-third-party-crates/configuring-gnrt-config-toml.html", + "chromium/adding-third-party-crates/resolving-problems.html", + "chromium/adding-third-party-crates/downloading-crates.html", + "chromium/adding-third-party-crates/depending-on-a-crate.html", + "chromium/adding-third-party-crates/reviews-and-audits.html", + "chromium/adding-third-party-crates/resolving-problems/build-scripts-which-generate-code.html", + "chromium/adding-third-party-crates/resolving-problems/build-scripts-which-take-arbitrary-actions.html", + "chromium/adding-third-party-crates/checking-in.html", + "chromium/adding-third-party-crates/generating-gn-build-rules.html", + "chromium/adding-third-party-crates/keeping-up-to-date.html", + "chromium/adding-third-party-crates/configuring-cargo-toml.html", + "chromium/build-rules/depending.html", + "chromium/build-rules/vscode.html", + "chromium/build-rules/unsafe.html", + "chromium/testing.html", + "chromium/setup.html", + "chromium/build-rules.html", + "running-the-course.html", + "user-defined-types/const.html", + "user-defined-types/tuple-structs.html", + "user-defined-types/aliases.html", + "user-defined-types/static.html", + "user-defined-types/named-structs.html", + "user-defined-types/enums.html", + "tuples-and-arrays.html", + "modules.html", + "std-traits.html", + "404.html", + "pattern-matching.html", + "control-flow-basics/break-continue/labels.html", + "control-flow-basics/loops/for.html", + "control-flow-basics/loops/loop.html", + "control-flow-basics/blocks-and-scopes.html", + "control-flow-basics/loops.html", + "control-flow-basics/if.html", + "control-flow-basics/break-continue.html", + "control-flow-basics/macros.html", + "control-flow-basics/match.html", + "control-flow-basics/functions.html", + "android.html", + "welcome-day-4.html", + "thanks.html", + "welcome-day-4-afternoon.html", + "error-handling/result.html", + "error-handling/try.html", + "error-handling/anyhow.html", + "error-handling/panics.html", + "error-handling/thiserror.html", + "error-handling/try-conversions.html", + "error-handling/error.html", + "welcome-day-1.html", + "user-defined-types.html", + "glossary.html", + "testing.html", + "references.html", + "std-types.html", + "control-flow-basics.html", + "bare-metal.html", + "std-traits/read-and-write.html", + "std-traits/comparisons.html", + "std-traits/casting.html", + "std-traits/operators.html", + "std-traits/from-and-into.html", + "std-traits/default.html", + "welcome-day-2.html", +]; From 6806789e763a58802cb56afa1fb3117edefa5406 Mon Sep 17 00:00:00 2001 From: Michael Kerscher Date: Fri, 7 Mar 2025 16:27:11 +0100 Subject: [PATCH 2/4] tests: create exemption mechanism and ensure that exempted pages are removed once they don't violate the rules. remove more toc and print pages --- tests/src/recreate-slide.list.sh | 17 ------- tests/src/slide-size.test.ts | 57 ++++++++++++++++++----- tests/src/slides/recreate-slide.list.sh | 35 ++++++++++++++ tests/src/slides/slide-exemptions.list.ts | 18 +++++++ tests/src/slides/slides.list.ts | 2 - 5 files changed, 98 insertions(+), 31 deletions(-) delete mode 100755 tests/src/recreate-slide.list.sh create mode 100755 tests/src/slides/recreate-slide.list.sh create mode 100644 tests/src/slides/slide-exemptions.list.ts diff --git a/tests/src/recreate-slide.list.sh b/tests/src/recreate-slide.list.sh deleted file mode 100755 index 23fa00bc922a..000000000000 --- a/tests/src/recreate-slide.list.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -e -BASEDIR="$(dirname "$0")" - -pushd "${BASEDIR}/../../book/html/" -# exclude redirect pages, solutions and exercise -SLIDES=$(grep -L "Redirecting to..." -R --include "*.html" --exclude "exercise.html" --exclude "solution.html") -popd -OUTPUT="${BASEDIR}/slides/slides.list.ts" - -# create a ts module that can be imported in the tests -echo "export const slides = [" > ${OUTPUT}; -for SLIDE in ${SLIDES}; do -echo " \"${SLIDE}\"," >> ${OUTPUT}; -done; -echo "];" >> ${OUTPUT}; diff --git a/tests/src/slide-size.test.ts b/tests/src/slide-size.test.ts index 4b92cb910afc..e5cf198ec30e 100644 --- a/tests/src/slide-size.test.ts +++ b/tests/src/slide-size.test.ts @@ -1,20 +1,53 @@ import { describe, it } from "mocha"; import { $, expect, browser } from "@wdio/globals"; import { slides } from "./slides/slides.list"; +import { exemptions } from "./slides/slide-exemptions.list"; + +// these are empirically determined values in 16:9 ratio +const MAX_HEIGHT = 1333; +const MAX_WIDTH = 750; describe("Slide", () => { for (const slide of slides) { - it( - " " + - slide + - " should not be higher than 1333 pixels or wider than 750 pixels", - async () => { - await browser.url("/" + slide); - const main_element = $("#content > main"); - const main_element_size = await main_element.getSize(); - expect(main_element_size.height < 1333).toBe(true); - expect(main_element_size.width <= 750).toBe(true); - }, - ); + if (exemptions.includes(slide)) { + // This slide is exempted and violated rules before. + // It is expected to still do this and if not it should be removed from exemptions. + // This acts as a regression check + it( + " " + + slide + + " is on the exemption list but should be removed from slide-exemptions.list.ts", + async () => { + await browser.url("/" + slide); + const main_element = $("#content > main"); + const main_element_size = await main_element.getSize(); + console.info("slide " + slide + " is on the exemption list"); + // one of them (height, width) should fail + expect( + main_element_size.height >= MAX_HEIGHT || + main_element_size.width > MAX_WIDTH, + ).toBe(true); + }, + ); + } else { + it( + " " + + slide + + " should not be higher than " + + MAX_HEIGHT + + " pixels or wider than " + + MAX_WIDTH + + " pixels", + async () => { + await browser.url("/" + slide); + const main_element = $("#content > main"); + const main_element_size = await main_element.getSize(); + expect( + main_element_size.height < MAX_HEIGHT && + main_element_size.width <= MAX_WIDTH, + ).toBe(true); + }, + ); + } } }); diff --git a/tests/src/slides/recreate-slide.list.sh b/tests/src/slides/recreate-slide.list.sh new file mode 100755 index 000000000000..c38292209828 --- /dev/null +++ b/tests/src/slides/recreate-slide.list.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# This script recreates the slides.list.ts file based on the given book html directory. +# It is used to regenerate the list of slides that are tested in the slide-size.test.ts file. + +set -e +BASEDIR="$(dirname "$0")" + +if [[ "$1" == "" ]] ; then + echo "Usage: $0 " + exit 1 +fi +BOOKDIR="$1" + +pushd "${TEST_BOOK_DIR}" +# exclude special pages that should never be tested +SLIDES=$(grep -L "Redirecting to..." -R --include "*.html" \ + --exclude "exercise.html" \ + --exclude "solution.html" \ + --exclude "toc.html" \ + --exclude "print.html" \ + --exclude "404.html" \ + --exclude "glossary.html" \ + --exclude "index.html" \ + --exclude "course-structure.html" + ) +popd +OUTPUT="${BASEDIR}/slides.list.ts" + +# create a ts module that can be imported in the tests +echo "export const slides = [" > ${OUTPUT}; +for SLIDE in ${SLIDES}; do +echo " \"${SLIDE}\"," >> ${OUTPUT}; +done; +echo "];" >> ${OUTPUT}; diff --git a/tests/src/slides/slide-exemptions.list.ts b/tests/src/slides/slide-exemptions.list.ts new file mode 100644 index 000000000000..d868ff411623 --- /dev/null +++ b/tests/src/slides/slide-exemptions.list.ts @@ -0,0 +1,18 @@ +// These slides are known to violate the slide style guide. +// They are checked if they still violate and if not fail the test. +// Please remove slides that become good so they don't regress. +export const exemptions = [ + "android/interoperability/java.html", + "android/testing.html", + "bare-metal/aps/entry-point.html", + "exercises/bare-metal/compass.html", + "exercises/bare-metal/solutions-afternoon.html", + "exercises/bare-metal/rtc.html", + "exercises/bare-metal/solutions-morning.html", + "exercises/chromium/interoperability-with-cpp.html", + "exercises/chromium/bringing-it-together.html", + "concurrency/async-exercises/chat-app.html", + "concurrency/async-exercises/solutions.html", + "concurrency/sync-exercises/solutions.html", + "concurrency/sync-exercises/link-checker.html", +]; diff --git a/tests/src/slides/slides.list.ts b/tests/src/slides/slides.list.ts index 72c4b34a3c53..e68c0089ac1f 100644 --- a/tests/src/slides/slides.list.ts +++ b/tests/src/slides/slides.list.ts @@ -62,7 +62,6 @@ export const slides = [ "memory-management/approaches.html", "memory-management/move.html", "welcome-day-2-afternoon.html", - "toc.html", "welcome-day-3-afternoon.html", "index.html", "other-resources.html", @@ -129,7 +128,6 @@ export const slides = [ "closures/traits.html", "closures/capturing.html", "closures/syntax.html", - "print.html", "cargo.html", "borrowing/shared.html", "borrowing/interior-mutability.html", From d1853efa9e7e090939817e7be477f7837b894dfd Mon Sep 17 00:00:00 2001 From: Michael Kerscher Date: Fri, 7 Mar 2025 16:49:35 +0100 Subject: [PATCH 3/4] tests: add create slide list script to CI and remove generated list. This ensures new slides are checked. Developers can create them manually. --- .github/workflows/build.yml | 2 +- .gitignore | 3 + ...ate-slide.list.sh => create-slide.list.sh} | 14 +- tests/src/slides/slides.list.ts | 330 ------------------ 4 files changed, 14 insertions(+), 335 deletions(-) rename tests/src/slides/{recreate-slide.list.sh => create-slide.list.sh} (65%) delete mode 100644 tests/src/slides/slides.list.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf0f393080e6..9768bde9fbd5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -190,7 +190,7 @@ jobs: working-directory: ./tests - name: Test Javascript if: matrix.language == 'en' - run: npm test + run: ./src/slides/create-slide.list.sh; npm test env: TEST_BOOK_DIR: ../book/comprehensive-rust-${{ matrix.language }}/html working-directory: ./tests diff --git a/.gitignore b/.gitignore index aa0decb77b2f..24b52ca64296 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ crowdin.yml # Python virtualenv (for mdbook-slide-evaluator local installation) .venv/ + +# tests/ framework artifacts +tests/src/slide/slides/slides.list.ts diff --git a/tests/src/slides/recreate-slide.list.sh b/tests/src/slides/create-slide.list.sh similarity index 65% rename from tests/src/slides/recreate-slide.list.sh rename to tests/src/slides/create-slide.list.sh index c38292209828..10b77158ae43 100755 --- a/tests/src/slides/recreate-slide.list.sh +++ b/tests/src/slides/create-slide.list.sh @@ -1,16 +1,22 @@ #!/bin/bash -# This script recreates the slides.list.ts file based on the given book html directory. +# This script (re)creates the slides.list.ts file based on the given book html directory. # It is used to regenerate the list of slides that are tested in the slide-size.test.ts file. +# Takes either TEST_BOOK_DIR environment variable or first parameter as override. set -e BASEDIR="$(dirname "$0")" -if [[ "$1" == "" ]] ; then - echo "Usage: $0 " +if [[ -n "$1" ]]; then + # take directory from command line + TEST_BOOK_DIR="$1" +fi + +# check if TEST_BOOK_DIR is empty (not set by environment nor parameter) +if [[ -z "${TEST_BOOK_DIR}" ]]; then + echo "Usage: $0 " exit 1 fi -BOOKDIR="$1" pushd "${TEST_BOOK_DIR}" # exclude special pages that should never be tested diff --git a/tests/src/slides/slides.list.ts b/tests/src/slides/slides.list.ts deleted file mode 100644 index e68c0089ac1f..000000000000 --- a/tests/src/slides/slides.list.ts +++ /dev/null @@ -1,330 +0,0 @@ -export const slides = [ - "chromium.html", - "android/aidl.html", - "android/aidl/example-service/deploy.html", - "android/aidl/example-service/client.html", - "android/aidl/example-service/changing-implementation.html", - "android/aidl/example-service/changing-definition.html", - "android/aidl/example-service/interface.html", - "android/aidl/example-service/server.html", - "android/aidl/example-service/service.html", - "android/aidl/example-service/service-bindings.html", - "android/aidl/birthday-service.html", - "android/aidl/types/parcelables.html", - "android/aidl/types/arrays.html", - "android/aidl/types/primitives.html", - "android/aidl/types/objects.html", - "android/aidl/types/file-descriptor.html", - "android/aidl/types.html", - "android/interoperability/cpp.html", - "android/interoperability/with-c.html", - "android/interoperability/cpp/cpp-exception.html", - "android/interoperability/cpp/rust-result.html", - "android/interoperability/cpp/type-mapping.html", - "android/interoperability/cpp/bridge.html", - "android/interoperability/cpp/android-build-cpp.html", - "android/interoperability/cpp/cpp-bridge.html", - "android/interoperability/cpp/rust-bridge.html", - "android/interoperability/cpp/android-cpp-genrules.html", - "android/interoperability/cpp/shared-types.html", - "android/interoperability/cpp/generated-cpp.html", - "android/interoperability/cpp/android-build-rust.html", - "android/interoperability/cpp/shared-enums.html", - "android/interoperability/with-c/bindgen.html", - "android/interoperability/with-c/rust.html", - "android/interoperability/with-c/rust-library.html", - "android/interoperability/with-c/run-our-binary.html", - "android/interoperability/with-c/c-library.html", - "android/interoperability/java.html", - "android/testing/mocking.html", - "android/testing/googletest.html", - "android/interoperability.html", - "android/build-rules/binary.html", - "android/build-rules/library.html", - "android/testing.html", - "android/setup.html", - "android/build-rules.html", - "android/logging.html", - "references/shared.html", - "references/exclusive.html", - "references/slices.html", - "references/dangling.html", - "references/strings.html", - "hello-world/what-is-rust.html", - "hello-world/benefits.html", - "hello-world/playground.html", - "borrowing.html", - "memory-management/clone.html", - "memory-management/drop.html", - "memory-management/ownership.html", - "memory-management/copy-types.html", - "memory-management/review.html", - "memory-management/approaches.html", - "memory-management/move.html", - "welcome-day-2-afternoon.html", - "welcome-day-3-afternoon.html", - "index.html", - "other-resources.html", - "running-the-course/keyboard-shortcuts.html", - "running-the-course/course-structure.html", - "running-the-course/translations.html", - "iterators.html", - "welcome-day-1-afternoon.html", - "generics.html", - "hello-world.html", - "welcome-day-3.html", - "bare-metal/android/vmbase.html", - "bare-metal/alloc.html", - "bare-metal/useful-crates.html", - "bare-metal/aps/mmio.html", - "bare-metal/aps/entry-point.html", - "bare-metal/aps/uart.html", - "bare-metal/aps/inline-assembly.html", - "bare-metal/aps/better-uart.html", - "bare-metal/aps/other-projects.html", - "bare-metal/aps/exceptions.html", - "bare-metal/aps/better-uart/using.html", - "bare-metal/aps/better-uart/bitflags.html", - "bare-metal/aps/better-uart/registers.html", - "bare-metal/aps/better-uart/driver.html", - "bare-metal/aps/logging/using.html", - "bare-metal/aps/logging.html", - "bare-metal/aps/uart/traits.html", - "bare-metal/aps/uart/using.html", - "bare-metal/aps.html", - "bare-metal/no_std.html", - "bare-metal/useful-crates/buddy_system_allocator.html", - "bare-metal/useful-crates/zerocopy.html", - "bare-metal/useful-crates/spin.html", - "bare-metal/useful-crates/aarch64-paging.html", - "bare-metal/useful-crates/tinyvec.html", - "bare-metal/android.html", - "bare-metal/microcontrollers.html", - "bare-metal/microcontrollers/mmio.html", - "bare-metal/microcontrollers/embedded-hal.html", - "bare-metal/microcontrollers/debugging.html", - "bare-metal/microcontrollers/pacs.html", - "bare-metal/microcontrollers/probe-rs.html", - "bare-metal/microcontrollers/other-projects.html", - "bare-metal/microcontrollers/board-support.html", - "bare-metal/microcontrollers/hals.html", - "bare-metal/microcontrollers/type-state.html", - "bare-metal/minimal.html", - "std-types/result.html", - "std-types/std.html", - "std-types/vec.html", - "std-types/string.html", - "std-types/docs.html", - "std-types/hashmap.html", - "std-types/option.html", - "pattern-matching/infallible.html", - "pattern-matching/let-control-flow/let-else.html", - "pattern-matching/let-control-flow/while-let.html", - "pattern-matching/let-control-flow/if-let.html", - "pattern-matching/destructuring-structs.html", - "pattern-matching/destructuring-enums.html", - "pattern-matching/let-control-flow.html", - "pattern-matching/match.html", - "closures/traits.html", - "closures/capturing.html", - "closures/syntax.html", - "cargo.html", - "borrowing/shared.html", - "borrowing/interior-mutability.html", - "borrowing/interior-mutability/cell.html", - "borrowing/interior-mutability/refcell.html", - "borrowing/examples.html", - "borrowing/borrowck.html", - "smart-pointers.html", - "methods-and-traits.html", - "testing/other.html", - "testing/lints.html", - "testing/unit-tests.html", - "tuples-and-arrays/tuples.html", - "tuples-and-arrays/destructuring.html", - "tuples-and-arrays/iteration.html", - "tuples-and-arrays/arrays.html", - "iterators/collect.html", - "iterators/motivation.html", - "iterators/intoiterator.html", - "iterators/helpers.html", - "iterators/iterator.html", - "credits.html", - "types-and-values.html", - "unsafe-rust/unsafe-functions/rust.html", - "unsafe-rust/unsafe-functions/extern-c.html", - "unsafe-rust/unsafe-functions/calling.html", - "unsafe-rust/unsafe-traits.html", - "unsafe-rust/unions.html", - "unsafe-rust/mutable-static.html", - "unsafe-rust/unsafe.html", - "unsafe-rust/dereferencing.html", - "unsafe-rust/unsafe-functions.html", - "closures.html", - "modules/filesystem.html", - "modules/visibility.html", - "modules/paths.html", - "modules/modules.html", - "modules/encapsulation.html", - "generics/generic-functions.html", - "generics/generic-data.html", - "generics/impl-trait.html", - "generics/dyn-trait.html", - "generics/trait-bounds.html", - "generics/generic-traits.html", - "unsafe-rust.html", - "lifetimes.html", - "smart-pointers/rc.html", - "smart-pointers/box.html", - "smart-pointers/trait-objects.html", - "exercises/bare-metal/afternoon.html", - "exercises/bare-metal/compass.html", - "exercises/bare-metal/morning.html", - "exercises/bare-metal/solutions-afternoon.html", - "exercises/bare-metal/rtc.html", - "exercises/bare-metal/solutions-morning.html", - "exercises/chromium/interoperability-with-cpp.html", - "exercises/chromium/bringing-it-together.html", - "exercises/chromium/solutions.html", - "exercises/chromium/third-party.html", - "exercises/chromium/testing.html", - "exercises/chromium/build-rules.html", - "error-handling.html", - "cargo/running-locally.html", - "cargo/rust-ecosystem.html", - "cargo/code-samples.html", - "types-and-values/inference.html", - "types-and-values/hello-world.html", - "types-and-values/variables.html", - "types-and-values/values.html", - "types-and-values/arithmetic.html", - "concurrency/async-pitfalls.html", - "concurrency/welcome-async.html", - "concurrency/async-exercises/chat-app.html", - "concurrency/async-exercises/dining-philosophers.html", - "concurrency/async-exercises/solutions.html", - "concurrency/send-sync.html", - "concurrency/channels.html", - "concurrency/async-exercises.html", - "concurrency/sync-exercises/dining-philosophers.html", - "concurrency/sync-exercises/solutions.html", - "concurrency/sync-exercises/link-checker.html", - "concurrency/threads/plain.html", - "concurrency/threads/scoped.html", - "concurrency/async-control-flow.html", - "concurrency/async-pitfalls/async-traits.html", - "concurrency/async-pitfalls/cancellation.html", - "concurrency/async-pitfalls/pin.html", - "concurrency/async-pitfalls/blocking-executor.html", - "concurrency/channels/unbounded.html", - "concurrency/channels/bounded.html", - "concurrency/channels/senders-receivers.html", - "concurrency/shared-state.html", - "concurrency/welcome.html", - "concurrency/sync-exercises.html", - "concurrency/async.html", - "concurrency/shared-state/arc.html", - "concurrency/shared-state/example.html", - "concurrency/shared-state/mutex.html", - "concurrency/send-sync/sync.html", - "concurrency/send-sync/send.html", - "concurrency/send-sync/marker-traits.html", - "concurrency/send-sync/examples.html", - "concurrency/threads.html", - "concurrency/async-control-flow/channels.html", - "concurrency/async-control-flow/select.html", - "concurrency/async-control-flow/join.html", - "concurrency/async/futures.html", - "concurrency/async/tasks.html", - "concurrency/async/async-await.html", - "concurrency/async/runtimes.html", - "concurrency/async/runtimes/tokio.html", - "methods-and-traits/traits.html", - "methods-and-traits/traits/associated-types.html", - "methods-and-traits/traits/supertraits.html", - "methods-and-traits/traits/implementing.html", - "methods-and-traits/deriving.html", - "methods-and-traits/methods.html", - "memory-management.html", - "lifetimes/struct-lifetimes.html", - "lifetimes/lifetime-annotations.html", - "lifetimes/lifetime-elision.html", - "chromium/policy.html", - "chromium/interoperability-with-cpp.html", - "chromium/cargo.html", - "chromium/testing/rust-gtest-interop.html", - "chromium/testing/chromium-import-macro.html", - "chromium/testing/build-gn.html", - "chromium/adding-third-party-crates.html", - "chromium/interoperability-with-cpp/error-handling-png.html", - "chromium/interoperability-with-cpp/example-bindings.html", - "chromium/interoperability-with-cpp/using-cxx-in-chromium.html", - "chromium/interoperability-with-cpp/error-handling.html", - "chromium/interoperability-with-cpp/limitations-of-cxx.html", - "chromium/interoperability-with-cpp/error-handling-qr.html", - "chromium/adding-third-party-crates/configuring-gnrt-config-toml.html", - "chromium/adding-third-party-crates/resolving-problems.html", - "chromium/adding-third-party-crates/downloading-crates.html", - "chromium/adding-third-party-crates/depending-on-a-crate.html", - "chromium/adding-third-party-crates/reviews-and-audits.html", - "chromium/adding-third-party-crates/resolving-problems/build-scripts-which-generate-code.html", - "chromium/adding-third-party-crates/resolving-problems/build-scripts-which-take-arbitrary-actions.html", - "chromium/adding-third-party-crates/checking-in.html", - "chromium/adding-third-party-crates/generating-gn-build-rules.html", - "chromium/adding-third-party-crates/keeping-up-to-date.html", - "chromium/adding-third-party-crates/configuring-cargo-toml.html", - "chromium/build-rules/depending.html", - "chromium/build-rules/vscode.html", - "chromium/build-rules/unsafe.html", - "chromium/testing.html", - "chromium/setup.html", - "chromium/build-rules.html", - "running-the-course.html", - "user-defined-types/const.html", - "user-defined-types/tuple-structs.html", - "user-defined-types/aliases.html", - "user-defined-types/static.html", - "user-defined-types/named-structs.html", - "user-defined-types/enums.html", - "tuples-and-arrays.html", - "modules.html", - "std-traits.html", - "404.html", - "pattern-matching.html", - "control-flow-basics/break-continue/labels.html", - "control-flow-basics/loops/for.html", - "control-flow-basics/loops/loop.html", - "control-flow-basics/blocks-and-scopes.html", - "control-flow-basics/loops.html", - "control-flow-basics/if.html", - "control-flow-basics/break-continue.html", - "control-flow-basics/macros.html", - "control-flow-basics/match.html", - "control-flow-basics/functions.html", - "android.html", - "welcome-day-4.html", - "thanks.html", - "welcome-day-4-afternoon.html", - "error-handling/result.html", - "error-handling/try.html", - "error-handling/anyhow.html", - "error-handling/panics.html", - "error-handling/thiserror.html", - "error-handling/try-conversions.html", - "error-handling/error.html", - "welcome-day-1.html", - "user-defined-types.html", - "glossary.html", - "testing.html", - "references.html", - "std-types.html", - "control-flow-basics.html", - "bare-metal.html", - "std-traits/read-and-write.html", - "std-traits/comparisons.html", - "std-traits/casting.html", - "std-traits/operators.html", - "std-traits/from-and-into.html", - "std-traits/default.html", - "welcome-day-2.html", -]; From 8dfa09ed0f7bb45b76c866683c044832bf94abc3 Mon Sep 17 00:00:00 2001 From: Michael Kerscher Date: Sat, 8 Mar 2025 08:34:18 +0100 Subject: [PATCH 4/4] remove mdbook-slide-evaluator - functionality is replaced by the slide-size.test.ts in the test framework --- Cargo.lock | 210 --------------- Cargo.toml | 1 - mdbook-slide-evaluator/Cargo.toml | 22 -- mdbook-slide-evaluator/README.md | 65 ----- mdbook-slide-evaluator/src/evaluator.rs | 327 ------------------------ mdbook-slide-evaluator/src/lib.rs | 16 -- mdbook-slide-evaluator/src/main.rs | 122 --------- mdbook-slide-evaluator/src/slides.rs | 54 ---- 8 files changed, 817 deletions(-) delete mode 100644 mdbook-slide-evaluator/Cargo.toml delete mode 100644 mdbook-slide-evaluator/README.md delete mode 100644 mdbook-slide-evaluator/src/evaluator.rs delete mode 100644 mdbook-slide-evaluator/src/lib.rs delete mode 100644 mdbook-slide-evaluator/src/main.rs delete mode 100644 mdbook-slide-evaluator/src/slides.rs diff --git a/Cargo.lock b/Cargo.lock index 50c48d604b30..1100cc15272e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,7 +263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", - "clap_derive", ] [[package]] @@ -288,18 +287,6 @@ dependencies = [ "clap", ] -[[package]] -name = "clap_derive" -version = "4.5.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "clap_lex" version = "0.7.4" @@ -326,27 +313,6 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" name = "control-flow-basics" version = "0.1.0" -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "time", - "version_check", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -437,27 +403,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "csv" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - [[package]] name = "cxx" version = "1.0.142" @@ -542,15 +487,6 @@ dependencies = [ "cxx-build", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive_more" version = "0.99.17" @@ -697,31 +633,6 @@ dependencies = [ "thiserror 2.0.11", ] -[[package]] -name = "fantoccini" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7722aeee9c2be6fa131166990295089d73d973012b758a2208b9ba51af5dd024" -dependencies = [ - "base64 0.22.0", - "cookie 0.18.1", - "futures-core", - "futures-util", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-tls", - "hyper-util", - "mime", - "openssl", - "serde", - "serde_json", - "time", - "tokio", - "url", - "webdriver", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -924,12 +835,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - [[package]] name = "globset" version = "0.4.14" @@ -1049,12 +954,6 @@ dependencies = [ "http 0.2.11", ] -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.3.9" @@ -1712,24 +1611,6 @@ dependencies = [ "pulldown-cmark 0.13.0", ] -[[package]] -name = "mdbook-slide-evaluator" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "csv", - "fantoccini", - "glob", - "log", - "pretty_env_logger", - "serde", - "strum", - "tokio", - "tokio-util", - "url", -] - [[package]] name = "memchr" version = "2.6.4" @@ -1891,12 +1772,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-modular" version = "0.6.1" @@ -2185,12 +2060,6 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2797,28 +2666,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.90", -] - [[package]] name = "subtle" version = "2.6.1" @@ -2992,37 +2839,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -3260,12 +3076,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - [[package]] name = "unicode-width" version = "0.1.11" @@ -3482,26 +3292,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webdriver" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144ab979b12d36d65065635e646549925de229954de2eb3b47459b432a42db71" -dependencies = [ - "base64 0.21.5", - "bytes", - "cookie 0.16.2", - "http 0.2.11", - "log", - "serde", - "serde_derive", - "serde_json", - "thiserror 1.0.69", - "time", - "unicode-segmentation", - "url", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 2452ca0753bd..a5ba12707cd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "mdbook-course", "mdbook-exerciser", - "mdbook-slide-evaluator", "src/android/testing", "src/bare-metal/useful-crates/allocator-example", "src/bare-metal/useful-crates/zerocopy-example", diff --git a/mdbook-slide-evaluator/Cargo.toml b/mdbook-slide-evaluator/Cargo.toml deleted file mode 100644 index d1b0d928f5a5..000000000000 --- a/mdbook-slide-evaluator/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "mdbook-slide-evaluator" -version = "0.1.0" -authors = ["Michael Kerscher "] -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/google/comprehensive-rust" -description = "A tool for evaluating mdbook slides by rendering the html pages and spot violations to the policies" - -[dependencies] -anyhow = "1.0.96" -clap = { version = "4.5.31", features = ["derive"] } -csv = "1.3.1" -fantoccini = "0.21.4" -glob = "0.3.2" -log = "0.4.26" -pretty_env_logger = "0.5.0" -serde = { version = "1.0.218", features = ["derive"] } -strum = { version = "0.27.1", features = ["derive"] } -tokio = { version = "1.43.0", features = ["full"] } -tokio-util = "0.7.13" -url = "2.5.4" diff --git a/mdbook-slide-evaluator/README.md b/mdbook-slide-evaluator/README.md deleted file mode 100644 index 43dbf80f722d..000000000000 --- a/mdbook-slide-evaluator/README.md +++ /dev/null @@ -1,65 +0,0 @@ -mdbook-slide-evaluator allows you to evaluate the rendered slides. This way one -can find if there is too much content on the slides and if sorted by size one -can focus on the worst violations first. - -# How to run - -## Start a WebDriver compatible browser - -### Alternative: Docker - -Start a -[selenium docker container](https://github.com/SeleniumHQ/docker-selenium?tab=readme-ov-file#quick-start) -and mount the book folder into the container at `/book/`: - -``` -$ docker run -d -p 4444:4444 -p 7900:7900 --volume /path/to/my/workspace/comprehensive-rust/book:/book --shm-size="2g" selenium/standalone-chromium:latest -``` - -As the tool is running with a different base directory, you can use a relative -directory e.g., `../book/`: - -``` -$ cargo run -- ../book -``` - -### Alternative: Local WebDriver browser with `webdriver-manager` - -Use [webdriver-manager](https://pypi.org/project/webdriver-manager/) to install -a e.g., a `chromedriver` onto your system with: - -``` -$ pip install selenium webdriver-manager -$ python3 -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options -from webdriver_manager.chrome import ChromeDriverManager - -driver = webdriver.Chrome(service=Service(ChromeDriverManager().install(), port=4444)) -# end the session when you are done. -``` - -You can provide the absolute path here as the browser has the same view on the -filesystem: - -``` -$ cargo run -- /path/to/my/workspace/comprehensive-rust/book -``` - -## Run mdbook-slide-size - -If a screenshot directory is provided, the tool can also create screenshots to -evaluate this manually. The tool always recursively grabs all `*.html` files -from the given directory and processes it. - -``` -cargo run -- --screenshot-dir screenshots ../book/html/ -``` - -# Roadmap - -To avoid a `docker mount`, try to build a data uri from the given slide. This -has the challenge that this contains links to local stylesheets that have to be -included. `css_inline` can be used for that and this already works (kind of). If -someone wants to take a stab at this, feel free to contact the author. diff --git a/mdbook-slide-evaluator/src/evaluator.rs b/mdbook-slide-evaluator/src/evaluator.rs deleted file mode 100644 index 2758368d2cab..000000000000 --- a/mdbook-slide-evaluator/src/evaluator.rs +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fs; -use std::io::Write as _; -use std::path::{Path, PathBuf}; - -use anyhow::anyhow; -use fantoccini::elements::Element; -use fantoccini::Client; -use log::{debug, warn}; -use serde::Serialize; -use strum::Display; -use tokio_util::sync::CancellationToken; -use url::Url; - -use crate::slides::{Book, Slide}; - -/// An Evaluator is used to render a book that is a collection of slides -/// and extract information from an element on that page. It further can -/// take a screenshot of this element and store it. A webclient instance is -/// created on creation and dropped once the Evaluator is dropped. -pub struct Evaluator<'a> { - /// webclient used to render html - webclient: Client, - /// selector for the element that is scored - element_selector: fantoccini::wd::Locator<'a>, - /// store screenshot in this directory if provided - screenshot_dir: Option, - /// html base uri to the source_dir used as a prefix for each page - html_base_url: Url, - /// base directory for all processed files - source_dir: PathBuf, - /// if this token is cancelled, the process needs to end gracefully - cancellation_token: CancellationToken, - /// the policy applied to the slides - slide_policy: SlidePolicy, -} - -/// element coordinates returned by the browser -#[derive(Debug)] -struct ElementSize { - /// the width of the element - width: f64, - /// the height of the element - height: f64, -} - -impl From<(f64, f64, f64, f64)> for ElementSize { - fn from(value: (f64, f64, f64, f64)) -> Self { - Self { width: value.2, height: value.3 } - } -} - -#[derive(Debug)] -/// holds the evaluation result for a slide -pub struct EvaluationResult { - /// metadata about the slide - slide: Slide, - /// the size of the main content element - element_size: ElementSize, - /// all policy violations - policy_violations: Vec, -} - -/// holds all evaluation results for a book -pub struct EvaluationResults { - /// metadata about the book - _book: Book, - /// the collected evaluation results - results: Vec, -} - -#[derive(Serialize)] -struct ExportFormat { - filename: PathBuf, - element_width: usize, - element_height: usize, - policy_violations: String, -} - -impl EvaluationResults { - /// export the evaluation results to the given csv file, overwrites if - /// allowed - pub fn export_csv( - &self, - file: &Path, - overwrite: bool, - violations_only: bool, - ) -> anyhow::Result<()> { - if file.exists() && !overwrite { - Err(anyhow!( - "Not allowed to overwrite existing evaluation results at {}", - file.display() - ))?; - }; - - let mut csv_writer = csv::Writer::from_path(file)?; - for result in &self.results { - if violations_only && result.policy_violations.is_empty() { - continue; - } - csv_writer.serialize(ExportFormat { - filename: (*result.slide.filename).to_path_buf(), - element_width: result.element_size.width.round() as usize, - element_height: result.element_size.height.round() as usize, - policy_violations: result - .policy_violations - .iter() - .map(PolicyViolation::to_string) - .collect::>() - .join(";"), - })?; - } - Ok(()) - } - - /// dump the results to stdout - pub fn export_stdout(&self, violations_only: bool) { - for result in &self.results { - if violations_only && result.policy_violations.is_empty() { - continue; - } - println!( - "{}: {}x{} [{}]", - result.slide.filename.display(), - result.element_size.width, - result.element_size.height, - result - .policy_violations - .iter() - .map(PolicyViolation::to_string) - .collect::>() - .join(";"), - ); - } - } -} - -impl<'a> Evaluator<'_> { - /// create a new instance with the provided config. - /// fails if the webclient cannot be created - pub fn new( - webclient: Client, - element_selector: &'a str, - screenshot_dir: Option, - html_base_url: Url, - source_dir: PathBuf, - cancellation_token: CancellationToken, - slide_policy: SlidePolicy, - ) -> Evaluator<'a> { - let element_selector = fantoccini::Locator::XPath(element_selector); - Evaluator { - webclient, - element_selector, - screenshot_dir, - html_base_url, - source_dir, - cancellation_token, - slide_policy, - } - } - - /// navigate the webdriver to the given url. - /// ensure that html_base_url is set before calling this - /// after this call the webdriver will see the content at the url - async fn webdriver_open_url(&self, url: &Url) -> Result<(), anyhow::Error> { - debug!("open url in webclient: {}", url); - self.webclient.goto(url.as_str()).await?; - Ok(()) - } - - /// evaluate the currently opened webpage return the selected content - /// element if available - async fn get_content_element_from_slide( - &self, - ) -> anyhow::Result> { - match self.webclient.find(self.element_selector).await { - Result::Ok(result) => Ok(Some(result)), - Result::Err(fantoccini::error::CmdError::Standard( - fantoccini::error::WebDriver { - error: fantoccini::error::ErrorStatus::NoSuchElement, - .. - }, - )) => anyhow::Ok(None), - Result::Err(error) => Err(anyhow!(error))?, - } - } - - /// extract the element coordinates from this element - async fn get_element_coordinates( - &self, - element: &Element, - ) -> anyhow::Result { - let coordinates = Into::::into(element.rectangle().await?); - Ok(coordinates) - } - - /// store the screenshot as png to the given path - fn store_screenshot( - &self, - screenshot: Vec, - filename: &Path, - ) -> anyhow::Result<()> { - let relative_filename = filename.strip_prefix(&self.source_dir)?; - let output_filename = self - .screenshot_dir - .as_ref() - .unwrap() - .join(relative_filename.with_extension("png")); - debug!("write screenshot to {}", output_filename.to_str().unwrap()); - - // create directories if necessary - let output_dir = output_filename.parent().unwrap(); - if !output_dir.exists() { - debug!("creating {}", output_dir.to_str().unwrap()); - fs::create_dir_all(output_dir)?; - } - - let mut file = - fs::OpenOptions::new().create(true).write(true).open(output_filename)?; - - file.write_all(&screenshot)?; - Ok(()) - } - - /// evaluate a single slide - pub async fn eval_slide( - &self, - slide: &Slide, - ) -> anyhow::Result> { - debug!("evaluating {:?}", slide); - - let url = self.html_base_url.join(&slide.filename.display().to_string())?; - self.webdriver_open_url(&url).await?; - - let Some(content_element) = self.get_content_element_from_slide().await? - else { - return Ok(None); - }; - let element_size = self.get_element_coordinates(&content_element).await?; - if self.screenshot_dir.is_some() { - let screenshot = content_element.screenshot().await?; - self.store_screenshot(screenshot, &slide.filename)?; - } - let policy_violations = self.slide_policy.eval_size(&element_size); - let result = EvaluationResult { - slide: slide.clone(), - element_size, - policy_violations, - }; - debug!("information about element: {:?}", result); - Ok(Some(result)) - } - - /// evaluate an entire book - pub async fn eval_book(&self, book: Book) -> anyhow::Result { - let mut results = vec![]; - debug!("slide count: {}", book.slides().len()); - for slide in book.slides().iter() { - if self.cancellation_token.is_cancelled() { - debug!("received cancel request, return already completed results"); - break; - } - let Some(result) = self.eval_slide(slide).await? else { - warn!("slide with no content - ignore: {:?}", slide); - continue; - }; - results.push(result); - } - Ok(EvaluationResults { _book: book, results }) - } -} - -/// all possible policy violations -#[derive(Debug, Display, Serialize)] -enum PolicyViolation { - /// violation of the maximum height - MaxWidth, - /// violation of the maximum width - MaxHeight, -} - -/// the SlidePolicy struct contains all parameters for evaluating a slide -pub struct SlidePolicy { - /// the maximum allowed width of a slide - pub max_width: usize, - /// the maximum allowed height of a slide - pub max_height: usize, -} - -impl SlidePolicy { - /// evaluate if the width is within the policy - fn eval_width(&self, element_size: &ElementSize) -> Option { - if element_size.width as usize > self.max_width { - return Some(PolicyViolation::MaxWidth); - } - return None; - } - - /// evaluate if the width is within the policy - fn eval_height(&self, element_size: &ElementSize) -> Option { - if element_size.height as usize > self.max_height { - return Some(PolicyViolation::MaxHeight); - } - return None; - } - - /// evaluate all size policies - fn eval_size(&self, element_size: &ElementSize) -> Vec { - [self.eval_height(element_size), self.eval_width(element_size)] - .into_iter() - .flatten() - .collect() - } -} diff --git a/mdbook-slide-evaluator/src/lib.rs b/mdbook-slide-evaluator/src/lib.rs deleted file mode 100644 index 0238c32c7ffe..000000000000 --- a/mdbook-slide-evaluator/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod evaluator; -pub mod slides; diff --git a/mdbook-slide-evaluator/src/main.rs b/mdbook-slide-evaluator/src/main.rs deleted file mode 100644 index b364275e3fd4..000000000000 --- a/mdbook-slide-evaluator/src/main.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::path::PathBuf; - -use clap::Parser; -use log::{debug, info}; -use mdbook_slide_evaluator::evaluator::{Evaluator, SlidePolicy}; -use mdbook_slide_evaluator::slides::Book; -use tokio_util::sync::CancellationToken; -use url::Url; - -#[derive(Parser)] -#[command(version, about, arg_required_else_help(true))] -struct Args { - /// the URI of the webdriver - #[arg(long, default_value_t=String::from("http://localhost:4444"))] - webdriver: String, - /// the XPath to element that is evaluated - #[arg(long, default_value_t=String::from(r#"//*[@id="content"]/main"#))] - element: String, - /// take screenshots of the content element if provided - #[arg(short, long)] - screenshot_dir: Option, - /// a base url that is used to render the files (relative to source_dir). - /// if you mount the slides at source_dir into / in a webdriver docker - /// container you can use the default - #[arg(long, default_value_t=Url::parse("file:///").unwrap())] - base_url: Url, - /// exports to csv file if provided, otherwise to stdout - #[arg(long)] - export: Option, - /// allows overwriting the export file - #[arg(long, default_value_t = false)] - overwrite: bool, - /// the height of the webclient that renders the slide - #[arg(long, default_value_t = 1920)] - webclient_width: u32, - /// the width of the webclient that renders the slide - #[arg(long, default_value_t = 1080)] - webclient_height: u32, - /// max width of a slide - #[arg(long, default_value_t = 750)] - width: usize, - /// max height of a slide - default height/width values have 16/9 ratio - #[arg(long, default_value_t = 1333)] - height: usize, - /// if set only violating slides are shown - #[arg(long, default_value_t = false)] - violations_only: bool, - /// directory of the book that is evaluated - source_dir: PathBuf, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // pretty env receives log level from RUST_LOG env variable - pretty_env_logger::init(); - - let args = Args::parse(); - - // gather information about the book from the filesystem - let book = Book::from_html_slides(args.source_dir.clone())?; - - // create a new webclient that is used by the evaluator - let webclient: fantoccini::Client = - fantoccini::ClientBuilder::native().connect(&args.webdriver).await?; - // use a defined window size for reproducible results - webclient.set_window_size(args.webclient_width, args.webclient_height).await?; - - let cancellation_token = CancellationToken::new(); - - let slide_policy = - SlidePolicy { max_width: args.width, max_height: args.height }; - - // create a new evaluator (connects to the provided webdriver) - let evaluator = Evaluator::new( - webclient.clone(), - &args.element, - args.screenshot_dir, - args.base_url, - args.source_dir.to_path_buf(), - cancellation_token.clone(), - slide_policy, - ); - - tokio::spawn(async move { - tokio::signal::ctrl_c().await.unwrap(); - info!("received CTRL+C"); - // send a cancel signal - cancellation_token.cancel(); - }); - - // evaluate each slide - let score_results = evaluator.eval_book(book).await?; - - if let Some(export_file) = args.export { - score_results.export_csv( - &export_file, - args.overwrite, - args.violations_only, - )?; - } else { - score_results.export_stdout(args.violations_only); - } - - // close webclient as otherwise the unclosed session cannot be reused - debug!("closing webclient"); - webclient.close().await?; - Ok(()) -} diff --git a/mdbook-slide-evaluator/src/slides.rs b/mdbook-slide-evaluator/src/slides.rs deleted file mode 100644 index f97e6070382b..000000000000 --- a/mdbook-slide-evaluator/src/slides.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -use log::debug; - -/// a slide is a page in the book -#[derive(Debug, Clone)] -pub struct Slide { - pub filename: Arc, -} - -/// a book is a collection of slides -pub struct Book { - /// the path to the root directory of this book - _source_dir: PathBuf, - /// the collection of slides - slides: Vec, -} - -impl Book { - /// create a book from all html files in the source_dir - pub fn from_html_slides(source_dir: PathBuf) -> anyhow::Result { - let mut slides = vec![]; - let files = glob::glob(&format!( - "{}/**/*.html", - source_dir.to_str().expect("invalid path") - ))?; - for file in files { - let slide = Slide { filename: file?.into() }; - debug!("add {:?}", slide); - slides.push(slide); - } - Ok(Book { _source_dir: source_dir, slides }) - } - - /// return a reference to the slides of this book - pub fn slides(&self) -> &[Slide] { - &self.slides - } -}