Skip to content

Commit fc011ef

Browse files
authored
Merge pull request #553 from jyn514/delete-the-panic-button
Fix occasional panic when visiting settings.html
2 parents 7e16fc6 + dd6d871 commit fc011ef

File tree

5 files changed

+98
-14
lines changed

5 files changed

+98
-14
lines changed

src/db/add_package.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ use failure::err_msg;
2222
/// Adds a package into database.
2323
///
2424
/// Package must be built first.
25+
///
26+
/// NOTE: `source_files` refers to the files originally in the crate,
27+
/// not the files generated by rustdoc.
2528
pub(crate) fn add_package_into_database(conn: &Connection,
2629
metadata_pkg: &MetadataPackage,
2730
source_dir: &Path,
2831
res: &BuildResult,
29-
files: Option<Json>,
32+
source_files: Option<Json>,
3033
doc_targets: Vec<String>,
3134
default_target: &Option<String>,
3235
cratesio_data: &CratesIoData,
@@ -78,7 +81,7 @@ pub(crate) fn add_package_into_database(conn: &Connection,
7881
&metadata_pkg.keywords.to_json(),
7982
&has_examples,
8083
&cratesio_data.downloads,
81-
&files,
84+
&source_files,
8285
&doc_targets.to_json(),
8386
&is_library,
8487
&res.rustc_version,
@@ -132,7 +135,7 @@ pub(crate) fn add_package_into_database(conn: &Connection,
132135
&metadata_pkg.keywords.to_json(),
133136
&has_examples,
134137
&cratesio_data.downloads,
135-
&files,
138+
&source_files,
136139
&doc_targets.to_json(),
137140
&is_library,
138141
&res.rustc_version,

src/db/file.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,15 @@ pub(super) fn s3_client() -> Option<S3Client> {
142142
))
143143
}
144144

145-
/// Adds files into database and returns list of files with their mime type in Json
145+
/// Store all files in a directory and return [[mimetype, filename]] as Json
146+
///
147+
/// If there is an S3 Client configured, store files into an S3 bucket;
148+
/// otherwise, stores files into the 'files' table of the local database.
149+
///
150+
/// The mimetype is detected using `magic`.
151+
///
152+
/// Note that this function is used for uploading both sources
153+
/// and files generated by rustdoc.
146154
pub fn add_path_into_database<P: AsRef<Path>>(conn: &Connection,
147155
prefix: &str,
148156
path: P)

src/test/fakes.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,25 @@ use crate::db::CratesIoData;
33
use crate::docbuilder::BuildResult;
44
use crate::utils::{Dependency, MetadataPackage, Target};
55
use failure::Error;
6-
use rustc_serialize::json::Json;
76

8-
pub(crate) struct FakeRelease<'db> {
9-
db: &'db TestDatabase,
7+
#[must_use = "FakeRelease does nothing until you call .create()"]
8+
pub(crate) struct FakeRelease<'a> {
9+
db: &'a TestDatabase,
1010
package: MetadataPackage,
1111
build_result: BuildResult,
12-
files: Option<Json>,
12+
/// name, content
13+
source_files: Vec<(&'a str, &'a [u8])>,
14+
/// name, content
15+
rustdoc_files: Vec<(&'a str, &'a [u8])>,
1316
doc_targets: Vec<String>,
1417
default_target: Option<String>,
1518
cratesio_data: CratesIoData,
1619
has_docs: bool,
1720
has_examples: bool,
1821
}
1922

20-
impl<'db> FakeRelease<'db> {
21-
pub(super) fn new(db: &'db TestDatabase) -> Self {
23+
impl<'a> FakeRelease<'a> {
24+
pub(super) fn new(db: &'a TestDatabase) -> Self {
2225
FakeRelease {
2326
db,
2427
package: MetadataPackage {
@@ -46,7 +49,8 @@ impl<'db> FakeRelease<'db> {
4649
build_log: "It works!".into(),
4750
successful: true,
4851
},
49-
files: None,
52+
source_files: Vec::new(),
53+
rustdoc_files: Vec::new(),
5054
doc_targets: Vec::new(),
5155
default_target: None,
5256
cratesio_data: CratesIoData {
@@ -81,15 +85,38 @@ impl<'db> FakeRelease<'db> {
8185
self
8286
}
8387

88+
pub(crate) fn rustdoc_file(mut self, path: &'a str, data: &'a [u8]) -> Self {
89+
self.rustdoc_files.push((path, data));
90+
self
91+
}
92+
8493
pub(crate) fn create(self) -> Result<i32, Error> {
8594
let tempdir = tempdir::TempDir::new("docs.rs-fake")?;
8695

96+
let upload_files = |prefix: &str, files: &[(&str, &[u8])]| {
97+
let path_prefix = tempdir.path().join(prefix);
98+
std::fs::create_dir(&path_prefix)?;
99+
100+
for (path, data) in files {
101+
let file = path_prefix.join(&path);
102+
std::fs::write(file, data)?;
103+
}
104+
105+
let prefix = format!("{}/{}/{}", prefix, self.package.name, self.package.version);
106+
crate::db::add_path_into_database(&self.db.conn(), &prefix, path_prefix)
107+
};
108+
109+
let rustdoc_meta = upload_files("rustdoc", &self.rustdoc_files)?;
110+
log::debug!("added rustdoc files {}", rustdoc_meta);
111+
let source_meta = upload_files("source", &self.source_files)?;
112+
log::debug!("added source files {}", source_meta);
113+
87114
let release_id = crate::db::add_package_into_database(
88115
&self.db.conn(),
89116
&self.package,
90117
tempdir.path(),
91118
&self.build_result,
92-
self.files,
119+
Some(source_meta),
93120
self.doc_targets,
94121
&self.default_target,
95122
&self.cratesio_data,

src/test/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@ use once_cell::unsync::OnceCell;
66
use postgres::Connection;
77
use reqwest::{Client, Method, RequestBuilder};
88
use std::sync::{Arc, Mutex, MutexGuard};
9+
use std::panic;
910

1011
pub(crate) fn wrapper(f: impl FnOnce(&TestEnvironment) -> Result<(), Error>) {
1112
let env = TestEnvironment::new();
12-
let result = f(&env);
13+
// if we didn't catch the panic, the server would hang forever
14+
let maybe_panic = panic::catch_unwind(panic::AssertUnwindSafe(|| f(&env)));
1315
env.cleanup();
16+
let result = match maybe_panic {
17+
Ok(r) => r,
18+
Err(payload) => panic::resume_unwind(payload),
19+
};
1420

1521
if let Err(err) = result {
1622
eprintln!("the test failed: {}", err);
@@ -24,6 +30,13 @@ pub(crate) fn wrapper(f: impl FnOnce(&TestEnvironment) -> Result<(), Error>) {
2430
}
2531
}
2632

33+
/// Make sure that a URL returns a status code between 200-299
34+
pub(crate) fn assert_success(path: &str, web: &TestFrontend) -> Result<(), Error> {
35+
let status = web.get(path).send()?.status();
36+
assert!(status.is_success(), "failed to GET {}: {}", path, status);
37+
Ok(())
38+
}
39+
2740
pub(crate) struct TestEnvironment {
2841
db: OnceCell<TestDatabase>,
2942
frontend: OnceCell<TestFrontend>,

src/web/rustdoc.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,12 @@ fn path_for_version(req_path: &[&str], target_name: &str, conn: &Connection) ->
311311
.expect("paths should be of the form <kind>.<name>.html")
312312
};
313313
// check if req_path[3] is the platform choice or the name of the crate
314+
// rustdoc generates a ../settings.html page, so if req_path[3] is not
315+
// the target, that doesn't necessarily mean it's a platform.
316+
// we also can't check if it's in TARGETS, since some targets have been
317+
// removed (looking at you, i686-apple-darwin)
314318
let concat_path;
315-
let crate_root = if req_path[3] != target_name {
319+
let crate_root = if req_path[3] != target_name && req_path.len() >= 5 {
316320
concat_path = format!("{}/{}", req_path[3], req_path[4]);
317321
&concat_path
318322
} else {
@@ -408,3 +412,32 @@ impl Handler for SharedResourceHandler {
408412
Err(IronError::new(Nope::ResourceNotFound, status::NotFound))
409413
}
410414
}
415+
416+
#[cfg(test)]
417+
mod test {
418+
use crate::test::*;
419+
#[test]
420+
// regression test for https://github.com/rust-lang/docs.rs/issues/552
421+
fn settings_html() {
422+
wrapper(|env| {
423+
let db = env.db();
424+
// first release works, second fails
425+
db.fake_release()
426+
.name("buggy").version("0.1.0")
427+
.build_result_successful(true)
428+
.rustdoc_file("settings.html", b"some data")
429+
.rustdoc_file("all.html", b"some data 2")
430+
.create()?;
431+
db.fake_release()
432+
.name("buggy").version("0.2.0")
433+
.build_result_successful(false)
434+
.create()?;
435+
let web = env.frontend();
436+
assert_success("/", web)?;
437+
assert_success("/crate/buggy/0.1.0/", web)?;
438+
assert_success("/buggy/0.1.0/settings.html", web)?;
439+
assert_success("/buggy/0.1.0/all.html", web)?;
440+
Ok(())
441+
});
442+
}
443+
}

0 commit comments

Comments
 (0)