{"id":19175755,"url":"https://github.com/handcraftedbits/edgeifier","last_synced_at":"2026-06-11T23:31:17.722Z","repository":{"id":57724801,"uuid":"140463326","full_name":"handcraftedbits/edgeifier","owner":"handcraftedbits","description":"A Java library for generating test values, particularly for edge cases","archived":false,"fork":false,"pushed_at":"2018-07-10T17:35:02.000Z","size":51,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-04T02:16:26.569Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/handcraftedbits.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-07-10T16:59:36.000Z","updated_at":"2018-07-10T17:28:11.000Z","dependencies_parsed_at":"2022-09-11T02:10:11.116Z","dependency_job_id":null,"html_url":"https://github.com/handcraftedbits/edgeifier","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/handcraftedbits/edgeifier","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/handcraftedbits%2Fedgeifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/handcraftedbits%2Fedgeifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/handcraftedbits%2Fedgeifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/handcraftedbits%2Fedgeifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/handcraftedbits","download_url":"https://codeload.github.com/handcraftedbits/edgeifier/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/handcraftedbits%2Fedgeifier/sbom","scorecard":{"id":454194,"data":{"date":"2025-08-11","repo":{"name":"github.com/handcraftedbits/edgeifier","commit":"c40c6546cd781bdb21618db726b6cecea6d3e1f1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"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/2 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":"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":"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":"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":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"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":"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":"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":"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":"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":"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":"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":"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-19T09:08:34.740Z","repository_id":57724801,"created_at":"2025-08-19T09:08:34.740Z","updated_at":"2025-08-19T09:08:34.740Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34222709,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"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":[],"created_at":"2024-11-09T10:24:57.513Z","updated_at":"2026-06-11T23:31:17.697Z","avatar_url":"https://github.com/handcraftedbits.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Edgeifier [![Maven](https://img.shields.io/maven-metadata/v/http/central.maven.org/maven2/com/handcraftedbits/edgeifier/edgeifier/maven-metadata.xml.svg)](https://mvnrepository.com/artifact/com.handcraftedbits.edgeifier/edgeifier/1.0.0) [![Build Status](https://travis-ci.org/handcraftedbits/edgeifier.svg?branch=master)](https://travis-ci.org/handcraftedbits/edgeifier) [![Coverage Status](https://coveralls.io/repos/github/handcraftedbits/edgeifier/badge.svg)](https://coveralls.io/github/handcraftedbits/edgeifier) [![Javadocs](https://javadoc.io/badge/com.handcraftedbits.edgeifier/edgeifier-api.svg)](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api)\n\nA Java library for generating test values, particularly for edge cases.\n\n# Background\n\nA regular occurrence in testing is the need to generate values that match a _specification_ (for example, \"all valid\nemail addresses\") and also values that violate that specification.  In both cases, particular attention must be paid to\n_edge cases_, or values that are at the limit (or _edge_) of a specification.  Edgeifier helps generate these values\nwith a simple, fluent Java API.\n\n# Features\n\n* Simple, fluent Java API that generates infinite streams of values\n* Generates primitive, String, and date values\n* Can generate custom value types\n* No runtime dependencies\n\n# Requirements\n\nEdgeifier requires Java 8 or later.\n\n# Usage\n\n## First Steps\n\nAdd the following dependencies to your `pom.xml` file:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.handcraftedbits.edgeifier\u003c/groupId\u003e\n  \u003cartifactId\u003eedgeifier-api\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.handcraftedbits.edgeifier\u003c/groupId\u003e\n  \u003cartifactId\u003eedgeifier-impl\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.0\u003c/version\u003e\n  \u003cscope\u003eruntime\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nThen, create an `Edgeifier` instance:\n\n```java\nEdgeifier edgeifier = new Edgeifier();\n```\n\nBy default, this will use the current time as a random seed.  In general though, you'll want to use a specific seed\nvalue to ensure that the same values are generated during every test run.  In that case, simply pass the seed value to\nthe `Edgeifier` constructor:\n\n```java\nEdgeifier edgeifier = new Edgeifier(123456L);\n```\n\n# Examples\n\n## Primitives\n\nYou can use the `Edgeifier` object to declare a _specification_ for any value type.  Once you have a specification, you\ncan then create an infinite [`Stream`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) of\nvalues.\n\nFor example, let's say we want to test that our `checkValue()` function can only accept values between `0` and `100`.\nFirst, we need to make a `Stream` of integers matching that specification:\n\n```java\nStream\u003cInteger\u003e matching = edgeifier.makeIntsLike().any().between(0, 101).stream(); // Note that maximum value is exclusive.\n```\n\nAlso, we'll want to make a `Stream` of integers that violate that specification:\n\n```java\nStream\u003cInteger\u003e violating = edgeifier.makeAnyOf(\n     edgeifier.makeIntsLike().any().lessThan(0),\n     edgeifier.makeIntsLike().any().atLeast(101)).stream();\n```\n\nFinally, we can use these `Stream`s to test our function (assume that `checkValue()` returns `true` if the value is\nwithin our defined range and `false` otherwise):\n\n```java\n@Test\npublic void testCheckValue () {\n     matching.limit(100).forEach(value -\u003e Assertions.assertTrue(checkValue(value)));\n     violating.limit(100).forEach(value -\u003e Assertions.assertFalse(checkValue(value)));\n}\n```\n\nNote that we use [`Stream.limit()`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#limit-long-)\nto limit the number of values generated; the `Stream`s created by Edgeifier are infinite by default.  You should use a\nlimit that makes sense in your situation.\n\nConsult the [Javadoc](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/value/primitive/package-summary.html)\nfor additional information on generating primitive types.\n\n## Strings\n\nEdgefier excels in creating `String`s of arbitrary complexity.  For example, let's assume that we have a URL validator\nthat will accept any URL accepted by `java.net.URL` with a maximum length of `1024` characters.  We want to test this\nvalidator with a variety of URLs, so let's use the following specification:\n\n* The scheme can be either `http` or `https`\n* The hostname can optionally start with `www`\n* The hostname can be between `4` and `20` characters in length and can end with `.com`, `.edu`, or `.net`\n* The port can be between `80` and `9000` or missing altogether\n* There can be between `1` and `4` path segments with length between `1` and `32` characters\n\nThe Edgefier API makes it easy to create this specification in code.  First, let's make a `ValueBuilder` for the port\nvalue:\n\n```java\nValueBuilder\u003cString\u003e ports = edgeifier.makeStringsLike()\n     .thisOne(\":\")\n     .plus()\n     .builder(edgeifier.makeIntsLike().any().between(80, 9001));\n```\n\nNotice what we're doing: concatenating the port specifier (\"`:`\") with the output of another `ValueBuilder` (the one\ngenerating random port values).\n\nWe'll also need to make a path segment `ValueBuilder`:\n\n```java\nValueBuilder\u003cString\u003e segments = edgeifier.makeStringsLike()\n     .thisOne(\"/\")\n     .plus()\n     .anyInRange('a', 'z').repeat(1, 33);\n```\n\nNow, let's combine that `ValueBuilder` with the one that will generate the rest of the URL:\n\n```java\nValueBuilder\u003cString\u003e urls = edgeifier.makeStringsLike()\n     .anyOf(\"http://\", \"https://\")\n     .plus()\n     .thisOne(\"www.\").optional()\n     .plus()\n     .anyInRange('a', 'z').repeat(4, 21)\n     .plus()\n     .anyOf(\".com\", \".edu\", \".net\")\n     .plus()\n     .builder(ports).optional()\n     .plus()\n     .builder(segments).repeat(1, 5);\n```\n\nNotice that we can generate `String`s of arbitrary complexity by combining fragments consisting of either generated\n`String` values or the output of other `ValueBuilder` objects via `plus()`.\n\nWhat kind of values will this `ValueBuilder` generate?  Let's test it out, but for sake of readability we'll use\n`Stream.filter()` to only return URLs that are exactly `60` characters in length:\n\n```java\nurls.stream().filter(value -\u003e value.length() == 60).limit(5).forEach(System.out::println);\n```\n\nHere's what we get:\n\n```\nhttp://ztctgwxyiabvfwkkn.edu/so/uqhdcmsulkmo/fxaodnssqufyhpx\nhttps://diabvmmmoduidpf.com/gfprsxg/dgshizrcabzkkhqeluspsnpn\nhttp://rybzux.edu/kdheasaxgqmquq/uapvgcpucntrdfdrmqiojhyzadu\nhttps://www.owvdy.com:5446/poarcmqikpbr/xznbnrjl/osfbigcvsrt\nhttps://www.pbjn.com:7384/psh/h/xauvauxnsxtwpwdaeu/uvzdexmze\n```\n\nTo _positively_ test our validator, we can simply use this `ValueBuilder` to generate any number of URLs up to `1024`\ncharacters in length.  Keeping in mind edge cases though, we should also specifically test URLs that are exactly `1024`\ncharacters in length.  To _negatively_ test our validator, we can use this same `ValueBuilder` and test with URLs of\nlength greater than `1024` characters.  We should also make a similar `ValueBuilder` that contains e.g., bad schemes,\ninvalid ports, etc.  More information about generating `String` values can be found in the\n[Javadoc](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/value/string/package-summary.html).\n\nAs an added convenience, Edgeifier includes a utility class to help generate characters from all of the Unicode\ncharacter classes.  See the [`UnicodeBuilder` Javadoc](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/util/UnicodeBuilder.html)\nfor more information.\n\n## Collections\n\nEdgeifier can be used to create collections (via `java.util.List`) of values of arbitrary length.  For example, we could\ncreate a `ValueBuilder` capable of generating lists of positive integers with lengths between `5` and `10` elements like\nso:\n\n```java\nValueBuilder\u003cList\u003cInteger\u003e\u003e lists = edgeifier.makeListsLike()\n     .any(edgeifier.makeIntsLike().any().atLeast(0))\n     .withLengthBetween(5, 11); // Note that maximum length is exclusive.\n```\n\nFor more information, see the [Javadoc](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/value/collection/package-summary.html).\n\n## Custom Types\n\nEdgeifier can generate custom types via \n[`CustomValueProvider`](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/value/custom/CustomValueProvider.html)\ninstances.  As an example, let's use Edgeifier to generate random instances of the following bean:\n\n```java\npublic class TestBean {\n     private int value;\n\n     public TestBean (int value) {\n          this.value = value;\n     }\n\n     public int getValue () {\n          return this.value;\n     }\n}\n```\n\nFirst, we need to create a `CustomValueProvider` capable of creating `TestBean` instances:\n\n```java\npublic class TestBeanValueProvider implements CustomValueProvider\u003cTestBean\u003e {\n     private int max = 10;\n\n     public TestBean generateValue (Edgeifier edgeifier) {\n          return new TestBean(edgeifier.makeIntsLike().any().between(0, this.max).stream().findFirst().get());\n     }\n\n     @Override\n     public void setProperty (String name, Object value) {\n          if (name.equals(\"max\")) {\n               this.max = (int) value;\n          }\n     }\n}\n```\n\nNotice the `setProperty` method: this is used to set arbitrary custom properties which control the generated value.  In\nthis case, if a property named `max` is set, we'll use that to control the maximum value associated with the generated\n`TestBean` instance.  The `generateValue` method is used to create our `TestBean` instance.  Notice that the current\nEdgeifier is provided.  With it, you can generate any type of value required to populate your bean.  In this example we\nare generating an infinite stream of integers between `0` and `max` and selecting the first element as the value for\n`TestBean.value`.\n\nNext, we need to create a \n[`CustomValueProviderFactory`](https://javadoc.io/doc/com.handcraftedbits.edgeifier/edgeifier-api/1.0.0/com/handcraftedbits/edgeifier/api/value/custom/CustomValueProviderFactory.html)\nclass that will help Edgeifier create instances of our `CustomValueProvider`:\n\n```java\npublic class TestBeanValueProviderFactory implements CustomValueProviderFactory\u003cTestBean\u003e {\n     @Override\n     public CustomValueProvider\u003cTestBean\u003e newCustomValueProvider () {\n          return new TestBeanValueProvider();\n     }\n\n     @Override\n     public Class\u003cTestBean\u003e valueClass () {\n          return TestBean.class;\n     }\n}\n```\n\nWe can register this class programmatically:\n\n```java\nedgeifier.registerCustomValueProviderFactory(new TestBeanValueProviderFactory());\n```\n\nOr by including a file named\n`META-INF/services/com.handcraftedbits.edgeifier.api.value.custom.CustomValueProviderFactory` containing the\nfully-qualified name of our `TestBeanValueProviderFactory` on the classpath.\n\nFinally, we can make use of this custom type with our Edgeifier instance:\n\n```java\nedgeifier.makeValuesLike().any(TestBean.class).stream() // Use the default max value of 10.\nedgeifier.makeValuesLike().any(TestBean.class).withProperty(\"max\", 100).stream(); // Use a custom max value.\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhandcraftedbits%2Fedgeifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhandcraftedbits%2Fedgeifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhandcraftedbits%2Fedgeifier/lists"}