added unit tests for write_shared
This commit is contained in:
parent
17c89239d9
commit
67663fc680
3 changed files with 256 additions and 50 deletions
|
|
@ -80,3 +80,6 @@ impl fmt::Display for EscapedJson {
|
|||
write!(f, "{}", json)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
|
|
@ -52,7 +51,7 @@ use crate::html::layout;
|
|||
use crate::html::render::search_index::build_index;
|
||||
use crate::html::render::search_index::SerializedSearchIndex;
|
||||
use crate::html::render::sorted_json::{EscapedJson, SortedJson};
|
||||
use crate::html::render::sorted_template::{self, SortedTemplate};
|
||||
use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
|
||||
use crate::html::render::{AssocItemLink, ImplRenderingParameters};
|
||||
use crate::html::static_files::{self, suffix_path};
|
||||
use crate::visit::DocVisitor;
|
||||
|
|
@ -78,33 +77,29 @@ pub(crate) fn write_shared(
|
|||
let crate_name = krate.name(cx.tcx());
|
||||
let crate_name = crate_name.as_str(); // rand
|
||||
let crate_name_json = SortedJson::serialize(crate_name); // "rand"
|
||||
let external_crates = hack_get_external_crate_names(cx)?;
|
||||
let external_crates = hack_get_external_crate_names(&cx.dst)?;
|
||||
let info = CrateInfo {
|
||||
src_files_js: SourcesPart::get(cx, &crate_name_json)?,
|
||||
search_index_js: SearchIndexPart::get(cx, index)?,
|
||||
search_index_js: SearchIndexPart::get(index, &cx.shared.resource_suffix)?,
|
||||
all_crates: AllCratesPart::get(crate_name_json.clone())?,
|
||||
crates_index: CratesIndexPart::get(&crate_name, &external_crates)?,
|
||||
trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
|
||||
type_impl: TypeAliasPart::get(cx, krate, &crate_name_json)?,
|
||||
};
|
||||
|
||||
let crates_info = vec![info]; // we have info from just one crate
|
||||
let crates = vec![info]; // we have info from just one crate. rest will found in out dir
|
||||
|
||||
write_static_files(cx, &opt)?;
|
||||
let dst = &cx.dst;
|
||||
if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) {
|
||||
if cx.include_sources {
|
||||
write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, &crates_info)?;
|
||||
write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, &crates)?;
|
||||
}
|
||||
write_rendered_cci::<SearchIndexPart, _>(
|
||||
SearchIndexPart::blank,
|
||||
dst,
|
||||
&crates_info,
|
||||
)?;
|
||||
write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, &crates_info)?;
|
||||
write_rendered_cci::<SearchIndexPart, _>(SearchIndexPart::blank, dst, &crates)?;
|
||||
write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, &crates)?;
|
||||
}
|
||||
write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, &crates_info)?;
|
||||
write_rendered_cci::<TypeAliasPart, _>(TypeAliasPart::blank, dst, &crates_info)?;
|
||||
write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, &crates)?;
|
||||
write_rendered_cci::<TypeAliasPart, _>(TypeAliasPart::blank, dst, &crates)?;
|
||||
match &opt.index_page {
|
||||
Some(index_page) if opt.enable_index_page => {
|
||||
let mut md_opts = opt.clone();
|
||||
|
|
@ -119,7 +114,7 @@ pub(crate) fn write_shared(
|
|||
write_rendered_cci::<CratesIndexPart, _>(
|
||||
|| CratesIndexPart::blank(cx),
|
||||
dst,
|
||||
&crates_info,
|
||||
&crates,
|
||||
)?;
|
||||
}
|
||||
_ => {} // they don't want an index page
|
||||
|
|
@ -189,7 +184,8 @@ fn write_search_desc(
|
|||
let path = path.join(filename);
|
||||
let part = SortedJson::serialize(&part);
|
||||
let part = format!("searchState.loadedDescShard({encoded_crate_name}, {i}, {part})");
|
||||
write_create_parents(&path, part)?;
|
||||
create_parents(&path)?;
|
||||
try_err!(fs::write(&path, part), &path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -286,8 +282,11 @@ else if (window.initSearch) window.initSearch(searchIndex);",
|
|||
)
|
||||
}
|
||||
|
||||
fn get(cx: &Context<'_>, search_index: SortedJson) -> Result<PartsAndLocations<Self>, Error> {
|
||||
let path = suffix_path("search-index.js", &cx.shared.resource_suffix);
|
||||
fn get(
|
||||
search_index: SortedJson,
|
||||
resource_suffix: &str,
|
||||
) -> Result<PartsAndLocations<Self>, Error> {
|
||||
let path = suffix_path("search-index.js", resource_suffix);
|
||||
let search_index = EscapedJson::from(search_index);
|
||||
Ok(PartsAndLocations::with(path, search_index))
|
||||
}
|
||||
|
|
@ -319,8 +318,8 @@ impl AllCratesPart {
|
|||
///
|
||||
/// This is to match the current behavior of rustdoc, which allows you to get all crates
|
||||
/// on the index page, even if --enable-index-page is only passed to the last crate.
|
||||
fn hack_get_external_crate_names(cx: &Context<'_>) -> Result<Vec<String>, Error> {
|
||||
let path = cx.dst.join("crates.js");
|
||||
fn hack_get_external_crate_names(doc_root: &Path) -> Result<Vec<String>, Error> {
|
||||
let path = doc_root.join("crates.js");
|
||||
let Ok(content) = fs::read_to_string(&path) else {
|
||||
// they didn't emit invocation specific, so we just say there were no crates
|
||||
return Ok(Vec::default());
|
||||
|
|
@ -361,7 +360,7 @@ impl CratesIndexPart {
|
|||
match SortedTemplate::magic(&template, MAGIC) {
|
||||
Ok(template) => template,
|
||||
Err(e) => panic!(
|
||||
"{e}: Object Replacement Character (U+FFFC) should not appear in the --index-page"
|
||||
"Object Replacement Character (U+FFFC) should not appear in the --index-page: {e}"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -860,6 +859,21 @@ impl Serialize for AliasSerializableImpl {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_path_parts<T: CciPart>(
|
||||
dst: &Path,
|
||||
crates_info: &[CrateInfo],
|
||||
) -> FxHashMap<PathBuf, Vec<String>> {
|
||||
let mut templates: FxHashMap<PathBuf, Vec<String>> = FxHashMap::default();
|
||||
crates_info.iter().map(|crate_info| crate_info.get::<T>().parts.iter()).flatten().for_each(
|
||||
|(path, part)| {
|
||||
let path = dst.join(&path);
|
||||
let part = part.to_string();
|
||||
templates.entry(path).or_default().push(part);
|
||||
},
|
||||
);
|
||||
templates
|
||||
}
|
||||
|
||||
/// Create all parents
|
||||
fn create_parents(path: &Path) -> Result<(), Error> {
|
||||
let parent = path.parent().expect("should not have an empty path here");
|
||||
|
|
@ -867,20 +881,13 @@ fn create_parents(path: &Path) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Create parents and then write
|
||||
fn write_create_parents(path: &Path, content: String) -> Result<(), Error> {
|
||||
create_parents(path)?;
|
||||
try_err!(fs::write(path, content), path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a blank template unless we could find one to append to
|
||||
fn read_template_or_blank<F, T: CciPart>(
|
||||
fn read_template_or_blank<F, T: FileFormat>(
|
||||
mut make_blank: F,
|
||||
path: &Path,
|
||||
) -> Result<SortedTemplate<T::FileFormat>, Error>
|
||||
) -> Result<SortedTemplate<T>, Error>
|
||||
where
|
||||
F: FnMut() -> SortedTemplate<T::FileFormat>,
|
||||
F: FnMut() -> SortedTemplate<T>,
|
||||
{
|
||||
match fs::read_to_string(&path) {
|
||||
Ok(template) => Ok(try_err!(SortedTemplate::from_str(&template), &path)),
|
||||
|
|
@ -898,27 +905,14 @@ fn write_rendered_cci<T: CciPart, F>(
|
|||
where
|
||||
F: FnMut() -> SortedTemplate<T::FileFormat>,
|
||||
{
|
||||
// read parts from disk
|
||||
let path_parts =
|
||||
crates_info.iter().map(|crate_info| crate_info.get::<T>().parts.iter()).flatten();
|
||||
// read previous rendered cci from storage, append to them
|
||||
let mut templates: FxHashMap<PathBuf, SortedTemplate<T::FileFormat>> = Default::default();
|
||||
for (path, part) in path_parts {
|
||||
let part = format!("{part}");
|
||||
let path = dst.join(&path);
|
||||
match templates.entry(path.clone()) {
|
||||
Entry::Vacant(entry) => {
|
||||
let template = read_template_or_blank::<_, T>(&mut make_blank, &path)?;
|
||||
let template = entry.insert(template);
|
||||
template.append(part);
|
||||
}
|
||||
Entry::Occupied(mut t) => t.get_mut().append(part),
|
||||
}
|
||||
}
|
||||
|
||||
// write the merged cci to disk
|
||||
for (path, template) in templates {
|
||||
for (path, parts) in get_path_parts::<T>(dst, crates_info) {
|
||||
create_parents(&path)?;
|
||||
// read previous rendered cci from storage, append to them
|
||||
let mut template = read_template_or_blank::<_, T::FileFormat>(&mut make_blank, &path)?;
|
||||
for part in parts {
|
||||
template.append(part);
|
||||
}
|
||||
let file = try_err!(File::create(&path), &path);
|
||||
let mut file = BufWriter::new(file);
|
||||
try_err!(write!(file, "{template}"), &path);
|
||||
|
|
@ -926,3 +920,6 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
206
src/librustdoc/html/render/write_shared/tests.rs
Normal file
206
src/librustdoc/html/render/write_shared/tests.rs
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
use crate::html::render::sorted_json::{EscapedJson, SortedJson};
|
||||
use crate::html::render::sorted_template::{Html, SortedTemplate};
|
||||
use crate::html::render::write_shared::*;
|
||||
|
||||
#[test]
|
||||
fn hack_external_crate_names() {
|
||||
let path = tempfile::TempDir::new().unwrap();
|
||||
let path = path.path();
|
||||
let crates = hack_get_external_crate_names(&path).unwrap();
|
||||
assert!(crates.is_empty());
|
||||
fs::write(path.join("crates.js"), r#"window.ALL_CRATES = ["a","b","c"];"#).unwrap();
|
||||
let crates = hack_get_external_crate_names(&path).unwrap();
|
||||
assert_eq!(crates, ["a".to_string(), "b".to_string(), "c".to_string()]);
|
||||
}
|
||||
|
||||
fn but_last_line(s: &str) -> &str {
|
||||
let (before, _) = s.rsplit_once("\n").unwrap();
|
||||
before
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sources_template() {
|
||||
let mut template = SourcesPart::blank();
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r"var srcIndex = new Map(JSON.parse('[]'));
|
||||
createSrcSidebar();"
|
||||
);
|
||||
template.append(EscapedJson::from(SortedJson::serialize("u")).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"var srcIndex = new Map(JSON.parse('["u"]'));
|
||||
createSrcSidebar();"#
|
||||
);
|
||||
template.append(EscapedJson::from(SortedJson::serialize("v")).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"var srcIndex = new Map(JSON.parse('["u","v"]'));
|
||||
createSrcSidebar();"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sources_parts() {
|
||||
let parts = SearchIndexPart::get(SortedJson::serialize(["foo", "bar"]), "suffix").unwrap();
|
||||
assert_eq!(&parts.parts[0].0, Path::new("search-indexsuffix.js"));
|
||||
assert_eq!(&parts.parts[0].1.to_string(), r#"["foo","bar"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_crates_template() {
|
||||
let mut template = AllCratesPart::blank();
|
||||
assert_eq!(but_last_line(&template.to_string()), r"window.ALL_CRATES = [];");
|
||||
template.append(EscapedJson::from(SortedJson::serialize("b")).to_string());
|
||||
assert_eq!(but_last_line(&template.to_string()), r#"window.ALL_CRATES = ["b"];"#);
|
||||
template.append(EscapedJson::from(SortedJson::serialize("a")).to_string());
|
||||
assert_eq!(but_last_line(&template.to_string()), r#"window.ALL_CRATES = ["a","b"];"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_crates_parts() {
|
||||
let parts = AllCratesPart::get(SortedJson::serialize("crate")).unwrap();
|
||||
assert_eq!(&parts.parts[0].0, Path::new("crates.js"));
|
||||
assert_eq!(&parts.parts[0].1.to_string(), r#""crate""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_index_template() {
|
||||
let mut template = SearchIndexPart::blank();
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r"var searchIndex = new Map(JSON.parse('[]'));
|
||||
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
|
||||
else if (window.initSearch) window.initSearch(searchIndex);"
|
||||
);
|
||||
template.append(EscapedJson::from(SortedJson::serialize([1, 2])).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r"var searchIndex = new Map(JSON.parse('[[1,2]]'));
|
||||
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
|
||||
else if (window.initSearch) window.initSearch(searchIndex);"
|
||||
);
|
||||
template.append(EscapedJson::from(SortedJson::serialize([4, 3])).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r"var searchIndex = new Map(JSON.parse('[[1,2],[4,3]]'));
|
||||
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
|
||||
else if (window.initSearch) window.initSearch(searchIndex);"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crates_index_part() {
|
||||
let external_crates = ["bar".to_string(), "baz".to_string()];
|
||||
let mut parts = CratesIndexPart::get("foo", &external_crates).unwrap();
|
||||
parts.parts.sort_by(|a, b| a.1.to_string().cmp(&b.1.to_string()));
|
||||
|
||||
assert_eq!(&parts.parts[0].0, Path::new("index.html"));
|
||||
assert_eq!(&parts.parts[0].1.to_string(), r#"<li><a href="bar/index.html">bar</a></li>"#);
|
||||
|
||||
assert_eq!(&parts.parts[1].0, Path::new("index.html"));
|
||||
assert_eq!(&parts.parts[1].1.to_string(), r#"<li><a href="baz/index.html">baz</a></li>"#);
|
||||
|
||||
assert_eq!(&parts.parts[2].0, Path::new("index.html"));
|
||||
assert_eq!(&parts.parts[2].1.to_string(), r#"<li><a href="foo/index.html">foo</a></li>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_alias_template() {
|
||||
let mut template = TraitAliasPart::blank();
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var implementors = Object.fromEntries([]);
|
||||
if (window.register_implementors) {
|
||||
window.register_implementors(implementors);
|
||||
} else {
|
||||
window.pending_implementors = implementors;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
template.append(SortedJson::serialize(["a"]).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var implementors = Object.fromEntries([["a"]]);
|
||||
if (window.register_implementors) {
|
||||
window.register_implementors(implementors);
|
||||
} else {
|
||||
window.pending_implementors = implementors;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
template.append(SortedJson::serialize(["b"]).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var implementors = Object.fromEntries([["a"],["b"]]);
|
||||
if (window.register_implementors) {
|
||||
window.register_implementors(implementors);
|
||||
} else {
|
||||
window.pending_implementors = implementors;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_alias_template() {
|
||||
let mut template = TypeAliasPart::blank();
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var type_impls = Object.fromEntries([]);
|
||||
if (window.register_type_impls) {
|
||||
window.register_type_impls(type_impls);
|
||||
} else {
|
||||
window.pending_type_impls = type_impls;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
template.append(SortedJson::serialize(["a"]).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var type_impls = Object.fromEntries([["a"]]);
|
||||
if (window.register_type_impls) {
|
||||
window.register_type_impls(type_impls);
|
||||
} else {
|
||||
window.pending_type_impls = type_impls;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
template.append(SortedJson::serialize(["b"]).to_string());
|
||||
assert_eq!(
|
||||
but_last_line(&template.to_string()),
|
||||
r#"(function() {
|
||||
var type_impls = Object.fromEntries([["a"],["b"]]);
|
||||
if (window.register_type_impls) {
|
||||
window.register_type_impls(type_impls);
|
||||
} else {
|
||||
window.pending_type_impls = type_impls;
|
||||
}
|
||||
})()"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_template_test() {
|
||||
let path = tempfile::TempDir::new().unwrap();
|
||||
let path = path.path().join("file.html");
|
||||
let make_blank = || SortedTemplate::<Html>::before_after("<div>", "</div>");
|
||||
|
||||
let template = read_template_or_blank(make_blank, &path).unwrap();
|
||||
assert_eq!(but_last_line(&template.to_string()), "<div></div>");
|
||||
fs::write(&path, template.to_string()).unwrap();
|
||||
let mut template = read_template_or_blank(make_blank, &path).unwrap();
|
||||
template.append("<img/>".to_string());
|
||||
fs::write(&path, template.to_string()).unwrap();
|
||||
let mut template = read_template_or_blank(make_blank, &path).unwrap();
|
||||
template.append("<br/>".to_string());
|
||||
fs::write(&path, template.to_string()).unwrap();
|
||||
let template = read_template_or_blank(make_blank, &path).unwrap();
|
||||
|
||||
assert_eq!(but_last_line(&template.to_string()), "<div><br/><img/></div>");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue