{"id":43045440,"url":"https://github.com/as/etch","last_synced_at":"2026-01-31T09:46:43.287Z","repository":{"id":57486353,"uuid":"103131722","full_name":"as/etch","owner":"as","description":"Write graphical regression tests in Go","archived":false,"fork":false,"pushed_at":"2018-05-05T01:55:21.000Z","size":32,"stargazers_count":10,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-13T21:36:48.262Z","etag":null,"topics":["automated","go","graphical","png","regression","tests"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/as.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":"2017-09-11T12:14:04.000Z","updated_at":"2022-11-08T01:17:11.000Z","dependencies_parsed_at":"2022-09-01T22:31:25.011Z","dependency_job_id":null,"html_url":"https://github.com/as/etch","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/as/etch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/as","download_url":"https://codeload.github.com/as/etch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fetch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28937607,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T08:53:31.997Z","status":"ssl_error","status_checked_at":"2026-01-31T08:51:38.521Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["automated","go","graphical","png","regression","tests"],"created_at":"2026-01-31T09:46:42.649Z","updated_at":"2026-01-31T09:46:43.277Z","avatar_url":"https://github.com/as.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Etch\n[![Go Report Card](https://goreportcard.com/badge/github.com/as/etch)](https://goreportcard.com/badge/github.com/as/etch)\n\nPackage etch provides a simple facility to write graphical regression tests.\nThe `Assert` function handles the common case. Give it the test variable, the\nimages you have and want, and it will fail your case if they differ.\n\n# Synopsis\n\n```\nhave = image.NewRGBA(r)\nwant = image.NewRGBA(r)\n\n// fail the test if the images differ, and write the delta to a png\netch.Assert(t, have, want, \"Test.png\")\n```\n\n# Visualize\n\nOptionally, provide a filename to store the graphical difference as an uncompressed PNG if the test fails.\n\n![paint](img/delta.png)\n\nThe `Extra` data in the image (have but don't want) is represented in `Red`.\nThe `Missing` data (`want`, but dont `have`) is represented in `Blue`. \nThese can be changed by modifying `Extra` and `Missing` package variables\n\n# Example\n\nI observed a bug in A where the text on the last line wasn't cleaned up unless that last line ended in a newline character.\nThis means if the frame displays `^a\\nb\\nc$` and `b\\n` is deleted, the user would see `^a\\nc\\nc$`. Nasty.\n\nWe can programatically check for any defect as long as we know how to reproduce it. \n\n# Step 1: Find Reproduction and Expected Result\nFind the reproduction. In this case I also found steps that generate the expected result. You can also use a cached expected result from a previously known good configuration.\n\n- Insert the multi-line text containing no trailing newline (good: insert a trailing newline)\n\n![paint](img/1.png)\n\n- Select any line but the last \n\n![paint](img/2.png)\n\n- Delete the selection\n\n![paint](img/3.png)\n\nAbove you can see the result of the middle line's deletion for both sessions. The window that\ndid not have the trailing newline did not clean up the last line after copying it up toward the\ntop of the frame\n\n# Step 2: Create Images\n\nCreate two images\n\n```\n\thave = image.NewRGBA(r)\n\twant = image.NewRGBA(r)\n```\n\nNow for the test case specific stuff. Your steps will replace mine\nbelow depending on what you're actually doing to the images. In my case\nthe frame draws on them directly, so we really don't care about\nits inner workings too much, just that there's a bug and we're\ngoing to test for its existence using these two images: `have`\nand `want`.\n\n\n```\n\t// Create two frames\n\th = New(r, font.NewBasic(fsize), have, A)\n\tw = New(r, font.NewBasic(fsize), want, A)\n\t\n\t// Insert some text with and without trailing newlines\n\tw.Insert([]byte(\"1234\\ncccc\\ndddd\\n\"), 0)\n\th.Insert([]byte(\"1234\\ncccc\\ndddd\"), 0)\n\t\n\t// Delete the second line\n\th.Delete(5, 10)\n\tw.Delete(5, 10)\n```\n\nBy this point, `want` will be an image with the _defect-free_\nstate and `have` will be an image with the _defective_ state\n\n```\n\tetch.Assert(t, have, want, \"TestDeleteLastLineNoNL.png\")\n```\n\n# Step 4: Go Test\n\nWe run `go test`\n\n```\n--- FAIL: TestDeleteLastLineNoNL (0.03s)\n\tetch.go:64: delta: TestDeleteLastLineNoNL.png\nFAIL\nexit status 1\nFAIL\tgithub.com/as/frame\t0.125s\n```\n\nWe can look at the image to see what went wrong: `TestDeleteLastLineNoNL.png`\n\n![paint](img/delta.png)\n\nAlthough it looks obvious, remember that this test would fail if any of the pixels differ. It's not easy to compare images visually, and you shouldn't avoid automating tests for it. Automating the tests helps prevent regressions from going undetected and speeds up the edit/compile/test cycle. \n\n# Step 5: Apply the Fix\n\n```\n\tf.Draw(f.b, image.Rect(pt0.X, pt0.Y, pt0.X+(f.r.Max.X-pt1.X), q0), f.b, pt1, f.op)\n\tf.Draw(f.b, image.Rect(f.r.Min.X, q0, f.r.Max.X, q0+(q2-q1)), f.b, image.Pt(f.r.Min.X, q1), f.op)\n\t// f.Paint(image.Pt(pt2.X, pt2.Y-(pt1.Y-pt0.Y)), pt2, f.Color.Back)\n\n```\n\nThe bug is the commented line above. Once the comment is removed, the test passes. Because `go test`\ncan be run automatically on file changes, this eliminates the manual step of checking the image. The\ntest passes once `have` and `want` are the same, and when they're not, just open the delta in an image\nviewer to see what went wrong.\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fas%2Fetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fetch/lists"}