Skip to content

Commit c7efaf2

Browse files
xinhaoyuancopybara-github
authored andcommitted
Add a Rust FuzzTest codelab
PiperOrigin-RevId: 986733795
1 parent d4da83d commit c7efaf2

3 files changed

Lines changed: 172 additions & 0 deletions

File tree

‎rust/codelab/BUILD‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# The Escaping Library example for the Rust FuzzTest Codelab.
16+
17+
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
18+
19+
package(default_visibility = ["//visibility:private"])
20+
21+
licenses(["notice"])
22+
23+
rust_library(
24+
name = "escaping",
25+
srcs = ["escaping.rs"],
26+
crate_root = "escaping.rs",
27+
edition = "2024",
28+
)
29+
30+
rust_test(
31+
name = "escaping_test",
32+
srcs = ["escaping_test.rs"],
33+
crate_root = "escaping_test.rs",
34+
edition = "2024",
35+
deps = [
36+
":escaping",
37+
"@com_google_fuzztest//rust:fuzztest",
38+
"@crate_index//:googletest",
39+
],
40+
)

‎rust/codelab/escaping.rs‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//! A tiny escaping library used by the Rust FuzzTest codelab.
16+
//!
17+
//! It converts special characters in a byte string into C-style escape
18+
//! sequences and back.
19+
//!
20+
//! The library intentionally contains two bugs that the codelab's fuzz tests
21+
//! are meant to discover, so please don't "fix" them.
22+
23+
/// Returns `input` with special characters replaced by C-style escape
24+
/// sequences.
25+
pub fn escape(input: &[u8]) -> Vec<u8> {
26+
let mut result = Vec::with_capacity(input.len());
27+
for &byte in input {
28+
match byte {
29+
b'\n' => result.extend_from_slice(br"\n"),
30+
b'\r' => result.extend_from_slice(br"\r"),
31+
b'\t' => result.extend_from_slice(br"\t"),
32+
b'\\' => result.extend_from_slice(br"\\"),
33+
_ => result.push(byte),
34+
}
35+
}
36+
result
37+
}
38+
39+
/// Returns `input` with C-style escape sequences replaced by the characters
40+
/// they represent.
41+
///
42+
/// Unrecognized escape sequences are dropped.
43+
pub fn unescape(input: &[u8]) -> Vec<u8> {
44+
let mut result = Vec::with_capacity(input.len());
45+
let mut i = 0;
46+
while i < input.len() {
47+
if input[i] == b'\\' {
48+
i += 1;
49+
match input[i] {
50+
b'n' => result.push(b'\n'),
51+
b't' => result.push(b'\t'),
52+
b'\\' => result.push(b'\\'),
53+
_ => {}
54+
}
55+
} else {
56+
result.push(input[i]);
57+
}
58+
i += 1;
59+
}
60+
result
61+
}

‎rust/codelab/escaping_test.rs‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use escaping::escape;
16+
use escaping::unescape;
17+
use googletest::prelude::*;
18+
19+
#[gtest]
20+
fn escaping_empty_input_returns_empty_output() {
21+
expect_that!(escape(b""), eq(b""));
22+
}
23+
24+
#[gtest]
25+
fn unescaping_empty_input_returns_empty_output() {
26+
expect_that!(unescape(b""), eq(b""));
27+
}
28+
29+
#[gtest]
30+
fn escaping_plain_input_is_returned_as_is() {
31+
expect_that!(escape(b"plain text"), eq(b"plain text"));
32+
}
33+
34+
#[gtest]
35+
fn unescaping_plain_input_is_returned_as_is() {
36+
expect_that!(unescape(b"plain text"), eq(b"plain text"));
37+
}
38+
39+
#[gtest]
40+
fn escaping_replaces_special_characters() {
41+
expect_that!(escape(b"two\nlines"), eq(br"two\nlines"));
42+
expect_that!(escape(b"back\\slash"), eq(br"back\\slash"));
43+
}
44+
45+
#[gtest]
46+
fn unescaping_replaces_escape_sequences() {
47+
expect_that!(unescape(br"two\nlines"), eq(b"two\nlines"));
48+
expect_that!(unescape(br"back\\slash"), eq(b"back\\slash"));
49+
}
50+
51+
// Uncomment the following imports and fuzz tests as you work through the
52+
// codelab.
53+
//
54+
// use fuzztest::domains::arbitrary::Arbitrary;
55+
// use fuzztest::domains::containers::VecOf;
56+
// use fuzztest::fuzztest;
57+
58+
// #[fuzztest(input = VecOf::new(Arbitrary::<u8>::default()))]
59+
// fn unescaping_escaped_input_gives_original(input: Vec<u8>) {
60+
// assert_eq!(unescape(&escape(&input)), input);
61+
// }
62+
63+
// #[fuzztest(input = VecOf::new(Arbitrary::<u8>::default()))]
64+
// fn escaping_never_panics(input: Vec<u8>) {
65+
// escape(&input);
66+
// }
67+
68+
// #[fuzztest(input = VecOf::new(Arbitrary::<u8>::default()))]
69+
// fn unescaping_never_panics(input: Vec<u8>) {
70+
// unescape(&input);
71+
// }

0 commit comments

Comments
 (0)