Skip to content
This repository was archived by the owner on Apr 21, 2023. It is now read-only.

Commit b72b757

Browse files
committed
test(classfile): enhance internal testing utility to test generated classes
1 parent c63acfe commit b72b757

File tree

6 files changed

+200
-85
lines changed

6 files changed

+200
-85
lines changed

javac-rs-classfile/src/bytecode.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
};
1212
use std::cmp::max;
1313

14-
use std::fs::read_to_string;
1514
use std::num::NonZeroU8;
1615
use thiserror::Error;
1716

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,79 @@
1-
use javac_rs_classfile::{Class, ClassfileWritable};
1+
use std::error::Error;
2+
use std::ffi::OsStr;
23
use std::fs::File;
4+
use std::io;
35
use std::path::Path;
6+
use std::process::{Command, Output};
7+
8+
use javac_rs_classfile::{Class, ClassfileWritable};
9+
10+
// TODO consider using result instead of panicing
11+
12+
pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
413

5-
pub fn dump_class<P: AsRef<Path>>(class: Class, file: P) {
6-
let path = file.as_ref();
14+
pub struct ClassTester {
15+
class_name: String,
16+
}
17+
18+
impl ClassTester {
19+
pub fn run(&self) -> Result<Output> {
20+
Ok(Command::new("java").arg(&self.class_name).output()?)
21+
}
22+
23+
pub fn run_with_args<I: IntoIterator<Item=S>, S: AsRef<OsStr>>(
24+
&self,
25+
args: I,
26+
) -> io::Result<Output> {
27+
Command::new("java")
28+
.args(args)
29+
.arg(&self.class_name)
30+
.output()
31+
}
32+
33+
pub fn disasm(&self) -> io::Result<Output> {
34+
Command::new("javap").arg(&self.class_name).output()
35+
}
36+
37+
pub fn disasm_with_args<I: IntoIterator<Item=S>, S: AsRef<OsStr>>(
38+
&self,
39+
args: I,
40+
) -> io::Result<Output> {
41+
Command::new("javap")
42+
.args(args)
43+
.arg(&self.class_name)
44+
.output()
45+
}
46+
47+
pub fn assert_disasmable(&self) {
48+
let result = self
49+
.disasm()
50+
.expect(&format!("Cannot execute javap for {}", self.class_name));
51+
assert_eq!(
52+
result
53+
.status
54+
.code()
55+
.expect("Class disassembly was terminated by a signal"),
56+
0,
57+
"Class could not be disassembled{}",
58+
String::from_utf8(result.stdout).map_or("".to_string(), |error| {
59+
let mut out = ": ".to_string();
60+
out.push_str(&error);
61+
out
62+
})
63+
)
64+
}
65+
}
66+
67+
pub fn dump_class(class: Class, class_name: String) -> Result<ClassTester> {
68+
let mut path = class_name.clone().replace('.', "/");
69+
path.push_str(".class");
70+
let path = Path::new(&path);
771
if let Some(parent) = path.parent() {
8-
std::fs::create_dir_all(parent).expect("Could not create parent directory");
72+
std::fs::create_dir_all(parent)?;
973
}
10-
let mut file = File::create(path).expect("Could not create file from path");
74+
let mut file = File::create(path)?;
1175

12-
println!("Writing class:\n{:#?}\nto file{:#?}", class, file);
1376
class.write_to_classfile(&mut file);
77+
78+
Ok(ClassTester { class_name })
1479
}

javac-rs-classfile/tests/class_with_attributes.rs

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use javac_rs_classfile::*;
21
use std::convert::TryFrom;
32
use std::fs::File;
43

4+
use javac_rs_classfile::*;
5+
6+
mod class_testing;
7+
58
#[test]
69
fn class_file_with_source_file_attribute() {
710
let mut class = Class::new(
@@ -17,11 +20,12 @@ fn class_file_with_source_file_attribute() {
1720
.unwrap();
1821
let class = class;
1922

20-
let mut file =
21-
File::create("ru/progrm_jarvis/javacrs/TestClassWithSourceFileAttribute.class").unwrap();
22-
println!("{:#?}", class);
23-
class.write_to_classfile(&mut file);
24-
println!("Written to file: {:#?}", file);
23+
class_testing::dump_class(
24+
class,
25+
"ru.progrm_jarvis.javacrs.TestClassWithSourceFileAttribute".to_string(),
26+
)
27+
.unwrap()
28+
.assert_disasmable();
2529
}
2630

2731
#[test]
@@ -35,11 +39,12 @@ fn class_file_with_synthetic_attribute() {
3539
class.add_synthetic_attribute().unwrap();
3640
let class = class;
3741

38-
let mut file =
39-
File::create("ru/progrm_jarvis/javacrs/TestClassWithSyntheticAttribute.class").unwrap();
40-
println!("{:#?}", class);
41-
class.write_to_classfile(&mut file);
42-
println!("Written to file: {:#?}", file);
42+
class_testing::dump_class(
43+
class,
44+
"ru.progrm_jarvis.javacrs.TestClassWithSyntheticAttribute".to_string(),
45+
)
46+
.unwrap()
47+
.assert_disasmable();
4348
}
4449

4550
#[test]
@@ -53,11 +58,12 @@ fn class_file_with_deprecated_attribute() {
5358
class.add_deprecated_attribute().unwrap();
5459
let class = class;
5560

56-
let mut file =
57-
File::create("ru/progrm_jarvis/javacrs/TestClassWithDeprecatedAttribute.class").unwrap();
58-
println!("{:#?}", class);
59-
class.write_to_classfile(&mut file);
60-
println!("Written to file: {:#?}", file);
61+
class_testing::dump_class(
62+
class,
63+
"ru.progrm_jarvis.javacrs.TestClassWithDeprecatedAttribute".to_string(),
64+
)
65+
.unwrap()
66+
.assert_disasmable();
6167
}
6268

6369
#[test]
@@ -73,11 +79,12 @@ fn class_file_with_signature_attribute() {
7379
.unwrap();
7480
let class = class;
7581

76-
let mut file =
77-
File::create("ru/progrm_jarvis/javacrs/TestClassWithSignatureAttribute.class").unwrap();
78-
println!("{:#?}", class);
79-
class.write_to_classfile(&mut file);
80-
println!("Written to file: {:#?}", file);
82+
class_testing::dump_class(
83+
class,
84+
"ru.progrm_jarvis.javacrs.TestClassWithSignatureAttribute".to_string(),
85+
)
86+
.unwrap()
87+
.assert_disasmable();
8188
}
8289

8390
#[test]
@@ -96,11 +103,12 @@ fn class_file_with_single_custom_attribute() {
96103
.unwrap();
97104
let class = class;
98105

99-
let mut file =
100-
File::create("ru/progrm_jarvis/javacrs/TestClassWithSingleCustomAttribute.class").unwrap();
101-
println!("{:#?}", class);
102-
class.write_to_classfile(&mut file);
103-
println!("Written to file: {:#?}", file);
106+
class_testing::dump_class(
107+
class,
108+
"ru.progrm_jarvis.javacrs.TestClassWithSingleCustomAttribute".to_string(),
109+
)
110+
.unwrap()
111+
.assert_disasmable();
104112
}
105113

106114
#[test]
@@ -125,10 +133,10 @@ fn class_file_with_multiple_custom_attributes() {
125133
.unwrap();
126134
let class = class;
127135

128-
let mut file =
129-
File::create("ru/progrm_jarvis/javacrs/TestClassWithMultipleCustomAttributes.class")
130-
.unwrap();
131-
println!("{:#?}", class);
132-
class.write_to_classfile(&mut file);
133-
println!("Written to file: {:#?}", file);
136+
class_testing::dump_class(
137+
class,
138+
"ru.progrm_jarvis.javacrs.TestClassWithMultipleCustomAttributes".to_string(),
139+
)
140+
.unwrap()
141+
.assert_disasmable();
134142
}

javac-rs-classfile/tests/class_with_fields.rs

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
use javac_rs_classfile::{
2-
major_versions, Class, ClassAccessFlag, ClassfileVersion, ClassfileWritable, ConstValue,
3-
FieldAccessFlag, FieldDescriptor, JvmVecU4,
4-
};
51
use std::convert::TryFrom;
62
use std::fs::File;
73

4+
use javac_rs_classfile::{
5+
Class, ClassAccessFlag, ClassfileVersion, ClassfileWritable, ConstValue, FieldAccessFlag,
6+
FieldDescriptor, JvmVecU4, major_versions,
7+
};
8+
9+
mod class_testing;
10+
811
#[test]
912
fn class_file_with_field_without_attributes() {
1013
let mut class = Class::new(
@@ -23,12 +26,14 @@ fn class_file_with_field_without_attributes() {
2326
FieldDescriptor::Int,
2427
)
2528
.unwrap();
29+
let class = class;
2630

27-
let mut file =
28-
File::create("ru/progrm_jarvis/javacrs/TestClassWithFieldWithoutAttributes.class").unwrap();
29-
println!("{:#?}", class);
30-
class.write_to_classfile(&mut file);
31-
println!("Written to file: {:#?}", file);
31+
class_testing::dump_class(
32+
class,
33+
"ru.progrm_jarvis.javacrs.TestClassWithFieldWithoutAttributes".to_string(),
34+
)
35+
.unwrap()
36+
.assert_disasmable();
3237
}
3338

3439
#[test]
@@ -52,12 +57,14 @@ fn class_file_with_field_with_const_value_attribute() {
5257
.unwrap();
5358
class.field_add_const_value_attribute(field, ConstValue::Integer(123));
5459
}
60+
let class = class;
5561

56-
let mut file =
57-
File::create("ru/progrm_jarvis/javacrs/TestClassWithConstValueAttribute.class").unwrap();
58-
println!("{:#?}", class);
59-
class.write_to_classfile(&mut file);
60-
println!("Written to file: {:#?}", file);
62+
class_testing::dump_class(
63+
class,
64+
"ru.progrm_jarvis.javacrs.TestClassWithConstValueAttribute".to_string(),
65+
)
66+
.unwrap()
67+
.assert_disasmable();
6168
}
6269

6370
#[test]
@@ -97,10 +104,12 @@ fn class_file_with_field_with_various_attributes() {
97104
)
98105
.unwrap();
99106
}
107+
let class = class;
100108

101-
let mut file =
102-
File::create("ru/progrm_jarvis/javacrs/TestClassWithVariousAttributes.class").unwrap();
103-
println!("{:#?}", class);
104-
class.write_to_classfile(&mut file);
105-
println!("Written to file: {:#?}", file);
109+
class_testing::dump_class(
110+
class,
111+
"ru.progrm_jarvis.javacrs.TestClassWithVariousAttributes".to_string(),
112+
)
113+
.unwrap()
114+
.assert_disasmable();
106115
}

javac-rs-classfile/tests/class_with_methods.rs

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
use std::convert::TryFrom;
2+
13
use javac_rs_classfile::{
2-
major_versions, Bytecode, Class, ClassAccessFlag, ClassfileVersion, ClassfileWritable,
3-
ConstValue, JvmVecU4, MethodAccessFlag,
4+
Bytecode, Class, ClassAccessFlag, ClassfileVersion, JvmVecU4, major_versions, MethodAccessFlag,
45
};
5-
use std::convert::TryFrom;
6-
use std::fs::File;
76

87
mod class_testing;
98

@@ -28,8 +27,8 @@ fn class_file_with_method_without_attributes() {
2827

2928
class_testing::dump_class(
3029
class,
31-
"ru/progrm_jarvis/javacrs/TestClassWithMethodWithoutAttributes.class",
32-
);
30+
"ru.progrm_jarvis.javacrs.TestClassWithMethodWithoutAttributes".to_string(),
31+
).unwrap().run();
3332
}
3433

3534
#[test]
@@ -68,11 +67,12 @@ fn class_file_with_method_with_various_attributes() {
6867
)
6968
.unwrap();
7069
}
70+
let class = class;
7171

7272
class_testing::dump_class(
7373
class,
74-
"ru/progrm_jarvis/javacrs/TestClassWithVariousAttributes.class",
75-
);
74+
"ru.progrm_jarvis.javacrs.TestClassWithVariousAttributes".to_string(),
75+
).unwrap().assert_disasmable();
7676
}
7777

7878
#[test]
@@ -100,11 +100,12 @@ fn class_file_with_method_with_code_attribute() {
100100
println!("Bytecode: {:?}", bytecode);
101101
class.method_add_code_attribute(method, bytecode);
102102
}
103+
let class = class;
103104

104105
class_testing::dump_class(
105106
class,
106-
"ru/progrm_jarvis/javacrs/TestClassWithCodeAttribute.class",
107-
);
107+
"ru.progrm_jarvis.javacrs.TestClassWithCodeAttribute".to_string(),
108+
).unwrap().assert_disasmable();
108109
}
109110

110111
#[test]
@@ -171,11 +172,17 @@ fn class_file_with_hello_world_main_method() {
171172

172173
class.method_add_code_attribute(method, bytecode).unwrap();
173174
}
175+
let class = class;
174176

175-
class_testing::dump_class(
177+
let result = class_testing::dump_class(
176178
class,
177-
"ru/progrm_jarvis/javacrs/ClassWithHelloWorldMainMethod.class",
178-
);
179+
"ru.progrm_jarvis.javacrs.ClassWithHelloWorldMainMethod".to_string(),
180+
)
181+
.unwrap()
182+
.run()
183+
.unwrap();
184+
assert_eq!(result.status.code().expect("Process terminated with signal"), 0);
185+
assert_eq!(result.stdout, b"Hello world!\n");
179186
}
180187

181188
// currently stack frame attributes are not set thus the class should be run using `-noverify`
@@ -249,9 +256,30 @@ fn class_file_with_naive_loop_in_main_method() {
249256

250257
class.method_add_code_attribute(method, bytecode).unwrap();
251258
}
259+
let class = class;
252260

253-
class_testing::dump_class(
261+
let result = class_testing::dump_class(
254262
class,
255-
"ru/progrm_jarvis/javacrs/ClassWithNaiveLoopInMainMethod.class",
263+
"ru.progrm_jarvis.javacrs.ClassWithNaiveLoopInMainMethod".to_string(),
264+
)
265+
.unwrap()
266+
.run_with_args(&["-noverify"])
267+
.unwrap();
268+
assert_eq!(
269+
result
270+
.status
271+
.code()
272+
.expect("Process terminated with signal"),
273+
0
274+
);
275+
assert_eq!(
276+
result.stdout,
277+
b"5\n\
278+
4\n\
279+
3\n\
280+
2\n\
281+
1\n\
282+
0\n\
283+
"
256284
);
257285
}

0 commit comments

Comments
 (0)