use beam_file::chunk;
use beam_file::chunk::Chunk;
use beam_file::parts;
use beam_file::BeamFile;
use beam_file::RawBeamFile;
use beam_file::Result;
use beam_file::StandardBeamFile;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;
#[test]
fn raw_chunks() {
let beam = RawBeamFile::from_file(test_file("test.beam")).unwrap();
let expected = vec![
"Atom", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT", "LocT", "Attr", "CInf", "Abst",
"Line",
];
assert_eq!(expected, collect_id(&beam.chunks));
let expected = vec![
"AtU8", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT", "LocT", "Attr", "CInf", "Dbgi",
"Docs", "ExDp", "Line",
];
let beam = RawBeamFile::from_file(test_file("Elixir.Unicode.beam")).unwrap();
assert_eq!(expected, collect_id(&beam.chunks));
}
#[test]
fn standard_chunks() {
use beam_file::chunk::StandardChunk::*;
macro_rules! find_chunk {
($beam:expr, $chunk:ident) => {
$beam
.chunks
.iter()
.filter_map(|c| {
if let $chunk(ref x) = *c {
Some(x)
} else {
None
}
})
.nth(0)
.unwrap()
};
}
let beam = StandardBeamFile::from_file(test_file("test.beam")).unwrap();
assert_eq!(
vec![
"Atom", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT", "LocT", "Attr", "CInf", "Abst",
"Line",
],
collect_id(&beam.chunks)
);
let atoms = &find_chunk!(beam, Atom).atoms;
assert_eq!(
vec![
"test",
"hello",
"ok",
"module_info",
"erlang",
"get_module_info",
"-hello/1-fun-0-",
"io",
"format",
],
atoms.iter().map(|a| &a.name).collect::<Vec<_>>()
);
let code = find_chunk!(beam, Code);
assert_eq!(16, code.info_size);
assert_eq!(0, code.version);
assert_eq!(153, code.opcode_max);
assert_eq!(9, code.label_count);
assert_eq!(4, code.function_count);
assert_eq!(91, code.bytecode.len());
let strt = find_chunk!(beam, StrT);
assert_eq!(0, strt.strings.len());
let atom_name = |id| &atoms[id as usize - 1].name;
let import_to_string = |i: &parts::Import| {
format!(
"{}:{}/{}",
atom_name(i.module),
atom_name(i.function),
i.arity
)
};
assert_eq!(
vec![
"erlang:get_module_info/1",
"erlang:get_module_info/2",
"io:format/2",
],
find_chunk!(beam, ImpT)
.imports
.iter()
.map(import_to_string)
.collect::<Vec<_>>()
);
let export_to_string =
|e: &parts::Export| format!("{}/{}@{}", atom_name(e.function), e.arity, e.label);
assert_eq!(
vec!["module_info/1@6", "module_info/0@4", "hello/1@2"],
find_chunk!(beam, ExpT)
.exports
.iter()
.map(export_to_string)
.collect::<Vec<_>>()
);
let fun_to_string = |f: &parts::Function| {
format!(
"{}/{}@{}.{}.{}.{}",
atom_name(f.function),
f.arity,
f.label,
f.index,
f.num_free,
f.old_uniq
)
};
assert_eq!(
vec!["-hello/1-fun-0-/1@8.0.1.38182595"],
find_chunk!(beam, FunT)
.functions
.iter()
.map(fun_to_string)
.collect::<Vec<_>>()
);
assert_eq!(
vec![13],
find_chunk!(beam, LitT)
.literals
.iter()
.map(|l| l.len())
.collect::<Vec<_>>()
);
let local_to_string =
|l: &parts::Local| format!("{}/{}@{}", atom_name(l.function), l.arity, l.label);
assert_eq!(
vec!["-hello/1-fun-0-/1@8"],
find_chunk!(beam, LocT)
.locals
.iter()
.map(local_to_string)
.collect::<Vec<_>>()
);
assert_eq!(40, find_chunk!(beam, Attr).term.len());
assert_eq!(209, find_chunk!(beam, CInf).term.len());
assert_eq!(307, find_chunk!(beam, Abst).term.len());
}
enum EncodeTestChunk {
Idempotent(chunk::StandardChunk),
Other(chunk::RawChunk),
}
impl chunk::Chunk for EncodeTestChunk {
fn id(&self) -> &chunk::Id {
use self::EncodeTestChunk::*;
match *self {
Idempotent(ref c) => c.id(),
Other(ref c) => c.id(),
}
}
fn decode_data<R: Read>(id: &chunk::Id, reader: R) -> Result<Self>
where
Self: Sized,
{
use self::EncodeTestChunk::*;
match id {
b"LitT" => Ok(Other(chunk::RawChunk::decode_data(id, reader)?)),
_ => Ok(Idempotent(chunk::StandardChunk::decode_data(id, reader)?)),
}
}
fn encode_data<W: Write>(&self, writer: W) -> Result<()> {
use self::EncodeTestChunk::*;
match *self {
Idempotent(ref c) => c.encode_data(writer),
Other(ref c) => c.encode_data(writer),
}
}
}
#[test]
fn encode_chunks() {
let mut original = Vec::new();
std::io::copy(
&mut File::open(test_file("test.beam")).unwrap(),
&mut original,
)
.unwrap();
let beam = BeamFile::<EncodeTestChunk>::from_reader(std::io::Cursor::new(&original)).unwrap();
let mut encoded = Vec::new();
beam.to_writer(&mut encoded).unwrap();
assert_eq!(original, encoded);
}
fn test_file(name: &str) -> PathBuf {
let mut path = PathBuf::from("tests/testdata/");
path.push(name);
path
}
fn collect_id<C: Chunk>(chunks: &[C]) -> Vec<String> {
chunks
.iter()
.map(|c| std::str::from_utf8(c.id()).unwrap().to_string())
.collect()
}