{"id":37026385,"url":"https://github.com/upthewaterspout/fates","last_synced_at":"2026-01-14T03:04:11.319Z","repository":{"id":57723786,"uuid":"151131488","full_name":"upthewaterspout/fates","owner":"upthewaterspout","description":"FATES (Find All Thread Execution Schedules) is a tool for testing multithreaded java applications","archived":false,"fork":false,"pushed_at":"2019-05-31T23:40:39.000Z","size":654,"stargazers_count":2,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-07-01T01:06:16.569Z","etag":null,"topics":["bytecode-manipulation","concurrency","java","multithreaded-tests","testing"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upthewaterspout.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-01T17:45:18.000Z","updated_at":"2019-07-24T19:11:15.000Z","dependencies_parsed_at":"2022-08-26T12:33:19.902Z","dependency_job_id":null,"html_url":"https://github.com/upthewaterspout/fates","commit_stats":null,"previous_names":[],"tags_count":3,"template":null,"template_full_name":null,"purl":"pkg:github/upthewaterspout/fates","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upthewaterspout%2Ffates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upthewaterspout%2Ffates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upthewaterspout%2Ffates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upthewaterspout%2Ffates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upthewaterspout","download_url":"https://codeload.github.com/upthewaterspout/fates/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upthewaterspout%2Ffates/sbom","scorecard":{"id":911540,"data":{"date":"2025-08-11","repo":{"name":"github.com/upthewaterspout/fates","commit":"6da115af07cb87eb900403ac3850e61eba1c685f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T19:23:38.540Z","repository_id":57723786,"created_at":"2025-08-24T19:23:38.541Z","updated_at":"2025-08-24T19:23:38.541Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408800,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode-manipulation","concurrency","java","multithreaded-tests","testing"],"created_at":"2026-01-14T03:04:10.595Z","updated_at":"2026-01-14T03:04:11.304Z","avatar_url":"https://github.com/upthewaterspout.png","language":"Java","readme":"[![Build Status](https://travis-ci.org/upthewaterspout/fates.svg?branch=master)](https://travis-ci.org/upthewaterspout/fates)\n\nFATES stands for Find All Thread Execution Schedules. It is a framework for\ntesting multi-threaded Java applications. \n\nFates contains two sub-modules\n\n* **fates-explore** - A utility that runs a test with *decision points* repeatedly, until\nall possible decisions are tested\n* **fates-threads** - A tool for finding race conditions in multi-threaded java code. It instruments the bytecode of the\nprogram under test and takes control of thread scheduling. It then runs the\ntest repeatedly until all possible scheduling orders are tested. This framework is in very early stages of development.\n\n# Installation\n\nFates is distributed through maven central. For multithreaded tests, just add the \n[fates-threads](https://search.maven.org/artifact/com.github.upthewaterspout.fates/fates-threads/) \njar as a test dependency.\n\n# How to use\n\n## Multithreaded tests\n\nIn your test code, run your multithreaded test using the \n[ThreadFates](https://upthewaterspout.github.io/fates/javadoc/fates-threads/index.html?com/github/upthewaterspout/fates/core/threading/ThreadFates.html) \nclass. For example, using junit, here is a simple test of whether it is safe for two threads to call ++ on an integer \nconcurrently (spoiler - it's not). Fates will run this test in all possible ways the two threads \ncan be interleaved. Some of these orderings result in an assertion error, showing us that this code \nis not threadsafe!\n\n```java\npublic class UnsynchronizedUpdateTest {\n\n  @Test()\n  public void incrementShouldBeThreadSafe() throws Throwable {\n    new ThreadFates().run(() -\u003e {\n      UnsafeInteger integer = new UnsafeInteger();\n      Thread thread1 = new Thread(integer::increment, \"thread1\");\n      Thread thread2 = new Thread(integer::increment, \"thread2\");\n      thread1.start();\n      thread2.start();\n      thread1.join();\n      thread2.join();\n\n      assertEquals(2, integer.getValue());\n    });\n  }\n\n  private static class UnsafeInteger {\n    int value = 0;\n\n    public void increment() {\n      value++;\n    }\n\n    public int getValue() {\n      return value;\n    }\n  }\n\n}\n```\n\nFates also includes a simple ParallelExecutor class that simplifies launching parallel threads\nand joing them in your test. It's designed to work well with the testing harness. The above test \ncan be simplified using this parallel executor like so.\n\n```java\n  @Test()\n  public void incrementShouldBeThreadSafe() throws Throwable {\n    new ThreadFates().run(() -\u003e {\n      UnsafeInteger integer = new UnsafeInteger();\n      new ParallelExecutor()\n              .inParallel(\"updater1\", updater::update)\n              .inParallel(\"updater2\", updater::update)\n              .run();\n\n      assertEquals(2, integer.getValue());\n    });\n  }\n\n``` \n\n\n## Repeating tests with decision points in them\nFates is not limited to testing multithreaded code. The ThreadFates harness is built on top of the more general purpose\n[Fates](https://upthewaterspout.github.io/fates/javadoc/fates-explore/index.html?com/github/upthewaterspout/fates/core/states/Fates.html) \nharness that allows for exploring the possible paths through a test that has many decision points. For example:\n\n```java\n  @Test\n  public void tryAllCombinations() {\n      new Fates()\n        .explore(decider -\u003e {\n          int a = decider.decide(\"a\", new HashSet\u003c\u003e(Arrays.asList(1,2,3,4,5)));\n          int b = decider.decide(\"b\", new HashSet\u003c\u003e(Arrays.asList(5,4,3,2,1)));\n          assertNotEquals(a, b);\n        });\n   }\n```\n\nThis test has `5^2` possible values for `a` and `b`. The test will be run repeatedly until it either fails or\nhas tried all possible choices for a and b.\n\nIt's possible to substitute different algorithms for exploring the space of possible ways the test runs\nby passing in a `StateExplorer` to the harness. For example there is a RandomExplorer than runs for a fixed\nnumber of iterations.\n\n# How it works\n\nThe harness launches your test in a separate JVM that has a custom java agent registered. This agent \nmodifies the bytecode of all classes in the test (including JDK classes) to take control of where\nthreads are launched and where state is accessed or modified. Using this instrumentation, the \nharness creates a scheduler that only allows one thread to be running at a time. \n\nEach field access is used as a point where the scheduler can choose to let\nthat thread continue, or schedule a different thread.\n\nOperations which might require a thread to block are also instrumented -\nsynchronization blocks, waits, etc. These operations are replaced by calls to\nthe scheduler.\n\nThe test is run repeatedly until all possible schedules are exercised.\n\n## Interesting classes\n * The `fates-explore` module with the `Fates` class and the `states` package - this package contains \n all of the logic to execute a test multiple times and explore all of the possible\n choices a test might make. This module does not do any bytecode instrumentation \n and isn't tied to multi-threaded testing.\n \n * Within the `fates-threads` module:\n   * `SharedStateSpaceScheduler` - this is the class that actually tries to\n control the order of the threads. It uses the `Decider` provided by the\n `StateExplorationHarness` to choose which thread to schedule at each point in\n time\n * Within the `fates-instrumentation` module:\n   * `ExecutionEventSingleton` - this class has all of the events that the bytecode \n instrumentation calls.\n   * `AsmTransformer` - This class builds the pipeline of `ClassVisitors` that actually\n modify the user's bytecode\n\n\n# Caveats\n\n## Long running tests\n\nTests that access many fields lead to a large number of possible thread\norderings. This means that tests may take a *very* long time to complete.\n\n## Classloading\nCurrently the scheduler is using a classloader that disables instrumentation\nduring classloading. That means it may miss race conditions that occur during\nclassloading. \n\n## Blocked thread handling\n\nLockSupport, synchronization, wait, notify, etc. are all handled by the\nscheduler. But there could be other events that cause a thread to block, for\nexample a blocking IO operation that depends on another thread. These\noperations will currently just cause the test to hang.\n\n## Timed waits\n\nTimed wait calls are currently no ops, because it's possible the thread in a timed\nwait could pick up and continue without other threads running.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupthewaterspout%2Ffates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupthewaterspout%2Ffates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupthewaterspout%2Ffates/lists"}