From b3a0d77fb3a4896b704b5fa4976ce7e98d1272f2 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 4 Jul 2016 13:27:01 -0700 Subject: [PATCH 1/2] circular-buffer: Add canonical data The circular-buffer exercise was first added in 2014 May at #9, with Ruby and Javascript as the initial implementing tracks: * https://github.com/exercism/xjavascript/pull/17 * https://github.com/exercism/xruby/pull/17 Implementing tracks: * https://github.com/exercism/xcsharp/blob/master/exercises/circular-buffer/CircularBufferTest.cs * https://github.com/exercism/xdlang/blob/master/exercises/circular-buffer/circular_buffer.d * https://github.com/exercism/xecmascript/blob/master/exercises/circular-buffer/circular-buffer.spec.js * https://github.com/exercism/xerlang/blob/master/exercises/circular-buffer/circular_buffer_tests.erl * https://github.com/exercism/xfsharp/blob/master/exercises/circular-buffer/CircularBufferTest.fs * https://github.com/exercism/xgo/blob/master/exercises/circular-buffer/circular_buffer_test.go * https://github.com/exercism/xjavascript/blob/master/exercises/circular-buffer/circular-buffer.spec.js * https://github.com/exercism/xlfe/blob/master/exercises/circular-buffer/test/circular-buffer-tests.lfe * https://github.com/exercism/xlua/blob/master/exercises/circular-buffer/circular-buffer_spec.lua * https://github.com/exercism/xpascal/blob/master/exercises/circular-buffer/uCircularBufferTests.pas * https://github.com/exercism/xpython/blob/master/exercises/circular-buffer/circular_buffer_test.py * https://github.com/exercism/xruby/blob/master/exercises/circular-buffer/circular_buffer_test.rb * https://github.com/exercism/xrust/blob/master/exercises/circular-buffer/tests/circular-buffer.rs All tracks pretty much implement the same tests, except: * Tracks typically have the "read item just written" and "each item can only be read once" tests combined. These tests split the two. * In dynamically-typed languages, buffers may be able to store heterogeneous types. This concept is not expressed in these tests. * In some statically-typed languages, buffers use generics, such that buffers may store any one type of the client's choosing. This concept is also not expressed in these tests. * The final test (ensuring that overwrite drops the right items) was more complex: capacity 5, 3 writes, 2 reads, write, read, 4 writes, 2 overwrites, 5 reads. It's been simplified to capacity 3, 3 writes, 1 read, write, overwrite, 3 reads. Closes https://github.com/exercism/todo/issues/79 --- exercises/circular-buffer/canonical-data.json | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 exercises/circular-buffer/canonical-data.json diff --git a/exercises/circular-buffer/canonical-data.json b/exercises/circular-buffer/canonical-data.json new file mode 100644 index 0000000000..fe6a731fa5 --- /dev/null +++ b/exercises/circular-buffer/canonical-data.json @@ -0,0 +1,296 @@ +{ + "#": [ + "In general, these circular buffers are expected to be stateful,", + "and each language will operate on them differently.", + "Tests tend to perform a series of operations, some of which expect a certain result.", + "As such, this common test suite can only say in abstract terms what should be done.", + "", + "Tests will contain a number of operations. The operation will be specified in the `operation` key.", + "Based on the operation, other keys may be present.", + "read: Reading from the buffer should produce the item located at `expected`.", + "write: Writing the item located at `item` should succeed.", + "readShouldFail: Reading from the buffer should fail.", + "writeShouldFail: Writing the item located at `item` should fail.", + "overwrite: Write the item located at `item` into the buffer, removing the oldest item if necessary.", + "clear: Clear the buffer.", + "", + "Finally, note that all values are integers.", + "If your language contains generics, you may consider allowing buffers to contain other types.", + "Tests for that are not included here.", + "" + ], + "cases": [ + { + "description": "reading empty buffer should fail", + "capacity": 1, + "operations": [ + { + "operation": "readShouldFail" + } + ] + }, + { + "description": "can read an item just written", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "read", + "expected": 1 + } + ] + }, + { + "description": "each item may only be read once", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "readShouldFail" + } + ] + }, + { + "description": "items are read in the order they are written", + "capacity": 2, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "read", + "expected": 2 + } + ] + }, + { + "description": "full buffer can't be written to", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "writeShouldFail", + "item": 2 + } + ] + }, + { + "description": "a read frees up capacity for another write", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "read", + "expected": 2 + } + ] + }, + { + "description": "read position is maintained even across multiple writes", + "capacity": 3, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "write", + "item": 3 + }, + { + "operation": "read", + "expected": 2 + }, + { + "operation": "read", + "expected": 3 + } + ] + }, + { + "description": "items cleared out of buffer can't be read", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "clear" + }, + { + "operation": "readShouldFail" + } + ] + }, + { + "description": "clear frees up capacity for another write", + "capacity": 1, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "clear" + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "read", + "expected": 2 + } + ] + }, + { + "description": "clear does nothing on empty buffer", + "capacity": 1, + "operations": [ + { + "operation": "clear" + }, + { + "operation": "write", + "item": 1 + }, + { + "operation": "read", + "expected": 1 + } + ] + }, + { + "description": "overwrite acts like write on non-full buffer", + "capacity": 2, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "overwrite", + "item": 2 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "read", + "expected": 2 + } + ] + }, + { + "description": "overwrite removes the oldest item on full buffer", + "capacity": 2, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "overwrite", + "item": 3 + }, + { + "operation": "read", + "expected": 2 + }, + { + "operation": "read", + "expected": 3 + } + ] + }, + { + "description": "overwrite doesn't remove an already-read item", + "capacity": 3, + "operations": [ + { + "operation": "write", + "item": 1 + }, + { + "operation": "write", + "item": 2 + }, + { + "operation": "write", + "item": 3 + }, + { + "operation": "read", + "expected": 1 + }, + { + "operation": "write", + "item": 4 + }, + { + "operation": "overwrite", + "item": 5 + }, + { + "operation": "read", + "expected": 3 + }, + { + "operation": "read", + "expected": 4 + }, + { + "operation": "read", + "expected": 5 + } + ] + } + ] +} From 43014f6db4ef582468278f52b6fcf667e213c17d Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Sat, 14 Jan 2017 11:07:38 -0800 Subject: [PATCH 2/2] circular-buffer: Add should_succeed to read and write This makes it clear that there are only four operations, instead of implying that `readShouldFail` and `writeShouldFail` are potentially separate operations. I convinced myself to do this since I argued against a `failure_detected` as a separate operation, but then realized that `readShouldFail` and `writeShouldFail` suffer from the same problems. --- exercises/circular-buffer/canonical-data.json | 107 +++++++++++++----- 1 file changed, 77 insertions(+), 30 deletions(-) diff --git a/exercises/circular-buffer/canonical-data.json b/exercises/circular-buffer/canonical-data.json index fe6a731fa5..987c9d113e 100644 --- a/exercises/circular-buffer/canonical-data.json +++ b/exercises/circular-buffer/canonical-data.json @@ -7,13 +7,16 @@ "", "Tests will contain a number of operations. The operation will be specified in the `operation` key.", "Based on the operation, other keys may be present.", - "read: Reading from the buffer should produce the item located at `expected`.", - "write: Writing the item located at `item` should succeed.", - "readShouldFail: Reading from the buffer should fail.", - "writeShouldFail: Writing the item located at `item` should fail.", + "read: Reading from the buffer should succeed if and only if `should_succeed` is true.", + " If it should succeed, it should produce the item at `expected`. ", + " If it should fail, `expected` will not be present. ", + "write: Writing the item located at `item` should succeed if and only if `should_succeed` is true.", "overwrite: Write the item located at `item` into the buffer, removing the oldest item if necessary.", "clear: Clear the buffer.", "", + "Failure of either `read` or `write` may be indicated in a manner appropriate for your language:", + "Raising an exception, returning (int, error), returning Option, etc.", + "", "Finally, note that all values are integers.", "If your language contains generics, you may consider allowing buffers to contain other types.", "Tests for that are not included here.", @@ -25,7 +28,8 @@ "capacity": 1, "operations": [ { - "operation": "readShouldFail" + "operation": "read", + "should_succeed": false } ] }, @@ -35,10 +39,12 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 } ] @@ -49,14 +55,17 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { - "operation": "readShouldFail" + "operation": "read", + "should_succeed": false } ] }, @@ -66,18 +75,22 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { "operation": "read", + "should_succeed": true, "expected": 2 } ] @@ -88,11 +101,13 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { - "operation": "writeShouldFail", - "item": 2 + "operation": "write", + "item": 2, + "should_succeed": false } ] }, @@ -102,18 +117,22 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 2 } ] @@ -124,26 +143,32 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { "operation": "write", - "item": 3 + "item": 3, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 2 }, { "operation": "read", + "should_succeed": true, "expected": 3 } ] @@ -154,13 +179,15 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "clear" }, { - "operation": "readShouldFail" + "operation": "read", + "should_succeed": false } ] }, @@ -170,17 +197,20 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "clear" }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 2 } ] @@ -194,10 +224,12 @@ }, { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 } ] @@ -208,7 +240,8 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "overwrite", @@ -216,10 +249,12 @@ }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { "operation": "read", + "should_succeed": true, "expected": 2 } ] @@ -230,11 +265,13 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "overwrite", @@ -242,10 +279,12 @@ }, { "operation": "read", + "should_succeed": true, "expected": 2 }, { "operation": "read", + "should_succeed": true, "expected": 3 } ] @@ -256,23 +295,28 @@ "operations": [ { "operation": "write", - "item": 1 + "item": 1, + "should_succeed": true }, { "operation": "write", - "item": 2 + "item": 2, + "should_succeed": true }, { "operation": "write", - "item": 3 + "item": 3, + "should_succeed": true }, { "operation": "read", + "should_succeed": true, "expected": 1 }, { "operation": "write", - "item": 4 + "item": 4, + "should_succeed": true }, { "operation": "overwrite", @@ -280,14 +324,17 @@ }, { "operation": "read", + "should_succeed": true, "expected": 3 }, { "operation": "read", + "should_succeed": true, "expected": 4 }, { "operation": "read", + "should_succeed": true, "expected": 5 } ]