| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use anyhow::{bail, Context, Error}; |
| use argh::FromArgs; |
| use log::{error, info, LevelFilter}; |
| use std::collections::HashMap; |
| use std::fs; |
| use std::path::{Path, PathBuf}; |
| use std::process; |
| |
| use rayon::prelude::*; |
| |
| use serde_json::{json, Value}; |
| |
| use libdoc::DocCompiler; |
| |
| mod fidljson; |
| use fidljson::{FidlJson, FidlJsonPackageData, TableOfContentsItem}; |
| |
| use simplelog::{Config, SimpleLogger}; |
| |
| mod templates; |
| use templates::html::HtmlTemplate; |
| use templates::markdown::MarkdownTemplate; |
| use templates::FidldocTemplate; |
| |
| static FIDLDOC_VERSION: &str = "0.0.4"; |
| static SUPPORTED_FIDLJSON: &str = "0.0.1"; |
| static FIDLDOC_CONFIG_PATH: &str = "fidldoc.config.json"; |
| |
| #[derive(Debug)] |
| enum TemplateType { |
| HTML, |
| Markdown, |
| } |
| |
| fn parse_template_type_str(value: &str) -> Result<TemplateType, String> { |
| match &value.to_lowercase()[..] { |
| "html" => Ok(TemplateType::HTML), |
| "markdown" => Ok(TemplateType::Markdown), |
| _ => Err("invalid template type".to_string()), |
| } |
| } |
| |
| #[derive(Debug, FromArgs)] |
| /// FIDL documentation generator. |
| struct Opt { |
| #[argh(option, short = 'c')] |
| /// path to a configuration file to provide additional options |
| config: Option<PathBuf>, |
| #[argh(option, default = "\"master\".to_string()")] |
| /// current commit hash, useful to coordinate doc generation with a specific source code revision |
| tag: String, |
| #[argh(positional)] |
| /// set the input file(s) to use |
| input: Vec<PathBuf>, |
| #[argh(option, short = 'o', default = "\"/tmp/fidldoc/\".to_string()")] |
| /// set the output folder |
| out: String, |
| #[argh(option, short = 'p', default = "\"/\".to_string()")] |
| /// set the base URL path for the generated docs |
| path: String, |
| #[argh( |
| option, |
| short = 't', |
| from_str_fn(parse_template_type_str), |
| default = "TemplateType::Markdown" |
| )] |
| /// select the template to use to render the docs |
| template: TemplateType, |
| #[argh(switch, short = 'v')] |
| /// generate verbose output |
| verbose: bool, |
| #[argh(switch)] |
| /// do not generate any output |
| silent: bool, |
| #[argh(switch)] |
| /// experimental documentation checks (do not generate any output) |
| /// TODO(fxbug.dev/71688) Remove the flag when the checks will be fully developed. |
| experimental_checks: bool, |
| } |
| |
| fn main() { |
| let opt: Opt = argh::from_env(); |
| if let Err(e) = run(opt) { |
| error!("Error: {}", e); |
| process::exit(1); |
| } |
| } |
| |
| fn run(opt: Opt) -> Result<(), Error> { |
| let mut input_files = opt.input; |
| normalize_input_files(&mut input_files); |
| |
| let output = &opt.out; |
| let output_path = PathBuf::from(output); |
| |
| let url_path = &opt.path; |
| |
| let template_type = &opt.template; |
| let template = select_template(template_type, &output_path) |
| .with_context(|| format!("Unable to instantiate template {:?}", template_type))?; |
| |
| if opt.silent && opt.verbose { |
| bail!("cannot use --silent and --verbose together"); |
| } |
| |
| if opt.verbose { |
| SimpleLogger::init(LevelFilter::Info, Config::default())?; |
| } else { |
| SimpleLogger::init(LevelFilter::Error, Config::default())?; |
| } |
| |
| // Read in fidldoc.config.json |
| let fidl_config_file = match opt.config { |
| Some(filepath) => filepath, |
| None => get_fidldoc_config_default_path() |
| .with_context(|| format!("Unable to retrieve default config file location"))?, |
| }; |
| info!("Using config file from {}", fidl_config_file.display()); |
| let fidl_config = read_fidldoc_config(&fidl_config_file) |
| .with_context(|| format!("Error parsing {}", &fidl_config_file.display()))?; |
| |
| create_output_dir(&output_path) |
| .with_context(|| format!("Unable to create output directory {}", output_path.display()))?; |
| |
| // Parse input files to get declarations, package set and fidl json map |
| let FidlJsonPackageData { declarations, fidl_json_map } = |
| process_fidl_json_files(input_files.to_vec()); |
| |
| if opt.experimental_checks { |
| // Only checks the documentation (no generation). |
| fidl_json_map |
| .par_iter() |
| .try_for_each(|(_package, package_fidl_json)| check_documentation(package_fidl_json)) |
| .expect("Errors"); |
| |
| if !opt.silent { |
| println!("documentation checked"); |
| } |
| } else { |
| // The table of contents lists all packages in alphabetical order. |
| let table_of_contents = create_toc(&fidl_json_map); |
| |
| // Modifications to the fidldoc object |
| let main_fidl_doc = json!({ |
| "table_of_contents": table_of_contents, |
| "fidldoc_version": FIDLDOC_VERSION, |
| "config": fidl_config, |
| "search": declarations, |
| "url_path": url_path, |
| }); |
| |
| // Create main page |
| template.render_main_page(&main_fidl_doc).expect("Unable to render main page"); |
| |
| let tag = &opt.tag; |
| let output_path_string = &output_path.display(); |
| fidl_json_map |
| .par_iter() |
| .try_for_each(|(package, package_fidl_json)| { |
| render_fidl_interface( |
| package, |
| package_fidl_json, |
| &table_of_contents, |
| &fidl_config, |
| &tag, |
| &declarations, |
| &url_path, |
| &template_type, |
| &output_path, |
| ) |
| }) |
| .expect("Unable to write FIDL reference files"); |
| |
| if !opt.silent { |
| println!("Generated documentation at {}", &output_path_string); |
| } |
| } |
| Ok(()) |
| } |
| |
| /// Checks all the documentation from the FIDL files. |
| /// |
| /// FIDL documentation always starts with 3 slashes and a space. That means that, for a declaration |
| /// which starts at column 1, the documentation starts 4 characters later at column 5. |
| /// |
| /// Declarations which are indented 4 characters start at column 5. That means that, for these |
| /// declarations, the documentation starts at column 9. |
| /// |
| /// The indentation is enforced by the FIDL linter/formatter. |
| fn check_documentation(package_fidl_json: &FidlJson) -> Result<(), String> { |
| let mut compiler = DocCompiler::new(); |
| for bits_declaration in package_fidl_json.bits_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &bits_declaration["maybe_attributes"], |
| &bits_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for members in bits_declaration["members"].as_array().iter() { |
| for member in members.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &member["maybe_attributes"], |
| &member["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for const_declaration in package_fidl_json.const_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &const_declaration["maybe_attributes"], |
| &const_declaration["location"], |
| /*column=*/ 5, |
| ); |
| } |
| for enum_declaration in package_fidl_json.enum_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &enum_declaration["maybe_attributes"], |
| &enum_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for members in enum_declaration["members"].as_array().iter() { |
| for member in members.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &member["maybe_attributes"], |
| &member["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for interface_declaration in package_fidl_json.interface_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &interface_declaration["maybe_attributes"], |
| &interface_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for methods in interface_declaration["methods"].as_array().iter() { |
| for method in methods.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &method["maybe_attributes"], |
| &method["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for struct_declaration in package_fidl_json.struct_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &struct_declaration["maybe_attributes"], |
| &struct_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for members in struct_declaration["members"].as_array().iter() { |
| for member in members.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &member["maybe_attributes"], |
| &member["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for table_declaration in package_fidl_json.table_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &table_declaration["maybe_attributes"], |
| &table_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for members in table_declaration["members"].as_array().iter() { |
| for member in members.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &member["maybe_attributes"], |
| &member["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for union_declaration in package_fidl_json.union_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &union_declaration["maybe_attributes"], |
| &union_declaration["location"], |
| /*column=*/ 5, |
| ); |
| for members in union_declaration["members"].as_array().iter() { |
| for member in members.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &member["maybe_attributes"], |
| &member["location"], |
| /*column=*/ 9, |
| ); |
| } |
| } |
| } |
| for type_alias_declaration in package_fidl_json.type_alias_declarations.iter() { |
| check_declaration_documentation( |
| &mut compiler, |
| &type_alias_declaration["maybe_attributes"], |
| &type_alias_declaration["location"], |
| /*column=*/ 5, |
| ); |
| } |
| |
| if !compiler.errors.is_empty() { |
| // All the documentation has been parsed for a JSON file. |
| // We encounered errors. |
| // Prints the errors discovered. |
| print!("{}", compiler.errors); |
| Err("Documentation has errors".to_string()) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| /// Checks some documentation associated to a declaration. |
| fn check_declaration_documentation( |
| compiler: &mut DocCompiler, |
| attributes_value: &Value, |
| location: &Value, |
| column: u32, |
| ) { |
| if let serde_json::Value::Object(location) = location { |
| for attributes in attributes_value.as_array().iter() { |
| for attribute in attributes.iter() { |
| if attribute["name"] == "Doc" { |
| if let serde_json::Value::String(text) = &attribute["value"] { |
| compiler.parse_doc( |
| location["filename"].to_string(), |
| infer_doc_line( |
| location["line"].to_string().parse::<u32>().unwrap_or(0), |
| &text, |
| ), |
| column, |
| clean_doc(&text), |
| ); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /// Infers the line number for the first line of documentation. |
| /// |
| /// This method assumes that the documentation is right before the declaration. |
| /// The column is hard coded because, for a kind of declaration, the column is always the same. |
| fn infer_doc_line(line: u32, text: &String) -> u32 { |
| line - (text.matches("\n").count() as u32) |
| } |
| |
| /// Cleans the documentation. |
| /// |
| /// This method removes the first character at the beginning of the first line (it should be a |
| /// space) and the first space at the beginning of all the other lines. |
| fn clean_doc(text: &String) -> String { |
| if text.len() == 0 { |
| "".to_owned() |
| } else { |
| // First removes the first character at the beginning of the text (the first line). Then |
| // removes all the spaces at the beginning of the other lines using replace. |
| text[1..].replace("\n ", "\n") |
| } |
| } |
| |
| fn render_fidl_interface( |
| package: &String, |
| package_fidl_json: &FidlJson, |
| table_of_contents: &Vec<TableOfContentsItem>, |
| fidl_config: &Value, |
| tag: &String, |
| declarations: &Vec<String>, |
| url_path: &String, |
| template_type: &TemplateType, |
| output_path: &PathBuf, |
| ) -> Result<(), Error> { |
| // Modifications to the fidldoc object |
| let fidl_doc = json!({ |
| "version": package_fidl_json.version, |
| "name": package_fidl_json.name, |
| "maybe_attributes": package_fidl_json.maybe_attributes, |
| "library_dependencies": package_fidl_json.library_dependencies, |
| "bits_declarations": package_fidl_json.bits_declarations, |
| "const_declarations": package_fidl_json.const_declarations, |
| "enum_declarations": package_fidl_json.enum_declarations, |
| "interface_declarations": package_fidl_json.interface_declarations, |
| "table_declarations": package_fidl_json.table_declarations, |
| "struct_declarations": package_fidl_json.struct_declarations, |
| "type_alias_declarations": package_fidl_json.type_alias_declarations, |
| "union_declarations": package_fidl_json.union_declarations, |
| "declaration_order": package_fidl_json.declaration_order, |
| "declarations": package_fidl_json.declarations, |
| "table_of_contents": table_of_contents, |
| "fidldoc_version": FIDLDOC_VERSION, |
| "config": fidl_config, |
| "tag": tag, |
| "search": declarations, |
| "url_path": url_path, |
| }); |
| |
| let template = select_template(&template_type, &output_path) |
| .with_context(|| format!("Unable to instantiate template {:?}", template_type)); |
| match template?.render_interface(&package, &fidl_doc) { |
| Err(why) => error!("Unable to render interface {}: {:?}", &package, why), |
| Ok(()) => info!("Generated interface documentation for {}", &package), |
| } |
| |
| Ok(()) |
| } |
| |
| fn select_template<'a>( |
| template_type: &TemplateType, |
| output_path: &'a PathBuf, |
| ) -> Result<Box<dyn FidldocTemplate + 'a>, Error> { |
| // Instantiate the template selected by the user |
| let template: Box<dyn FidldocTemplate> = match template_type { |
| TemplateType::HTML => { |
| let template = HtmlTemplate::new(&output_path)?; |
| Box::new(template) |
| } |
| TemplateType::Markdown => { |
| let template = MarkdownTemplate::new(&output_path); |
| Box::new(template) |
| } |
| }; |
| Ok(template) |
| } |
| |
| fn get_fidldoc_config_default_path() -> Result<PathBuf, Error> { |
| // If the fidldoc config file is not available, it should be found |
| // in the same directory as the executable. |
| // This needs to be calculated at runtime. |
| let fidldoc_executable = std::env::current_exe()?; |
| let fidldoc_execution_directory = fidldoc_executable.parent().unwrap(); |
| let fidl_config_default_path = fidldoc_execution_directory.join(FIDLDOC_CONFIG_PATH); |
| Ok(fidl_config_default_path) |
| } |
| |
| fn read_fidldoc_config(config_path: &Path) -> Result<Value, Error> { |
| let fidl_config_str = fs::read_to_string(config_path) |
| .with_context(|| format!("Couldn't open file {}", config_path.display()))?; |
| Ok(serde_json::from_str(&fidl_config_str)?) |
| } |
| |
| fn should_process_fidl_json(fidl_json: &FidlJson) -> bool { |
| if fidl_json.version != SUPPORTED_FIDLJSON { |
| error!( |
| "Error parsing {}: fidldoc does not support version {}, only {}", |
| fidl_json.name, fidl_json.version, SUPPORTED_FIDLJSON |
| ); |
| return false; |
| } |
| |
| if fidl_json.maybe_attributes.iter().any(|attr| attr["name"] == "NoDoc") { |
| info!("Skipping library with NoDoc attribute: {}", fidl_json.name); |
| return false; |
| } |
| |
| true |
| } |
| |
| fn process_fidl_json_files(input_files: Vec<PathBuf>) -> FidlJsonPackageData { |
| let mut package_data = FidlJsonPackageData::new(); |
| for file in input_files { |
| let fidl_file_path = PathBuf::from(&file); |
| let fidl_json = match FidlJson::from_path(&fidl_file_path) { |
| Err(why) => { |
| error!("Error parsing {}: {}", file.display(), why); |
| continue; |
| } |
| Ok(json) => json, |
| }; |
| |
| if should_process_fidl_json(&fidl_json) { |
| package_data.insert(fidl_json); |
| } |
| } |
| |
| // Sort declarations inside each package |
| package_data.fidl_json_map.par_iter_mut().for_each(|(_, package_fidl_json)| { |
| package_fidl_json.sort_declarations(); |
| }); |
| |
| package_data |
| } |
| |
| fn create_toc(fidl_json_map: &HashMap<String, FidlJson>) -> Vec<TableOfContentsItem> { |
| // The table of contents lists all packages in alphabetical order. |
| let mut table_of_contents: Vec<_> = fidl_json_map |
| .par_iter() |
| .map(|(package_name, fidl_json)| TableOfContentsItem { |
| name: package_name.clone(), |
| link: format!("{name}/index", name = package_name), |
| description: get_library_description(&fidl_json.maybe_attributes), |
| }) |
| .collect(); |
| table_of_contents.sort_unstable_by(|a, b| a.name.cmp(&b.name)); |
| table_of_contents |
| } |
| |
| fn get_library_description(maybe_attributes: &Vec<Value>) -> String { |
| for attribute in maybe_attributes { |
| if attribute["name"] == "Doc" { |
| return attribute["value"] |
| .as_str() |
| .expect("Unable to retrieve string value for library description") |
| .to_string(); |
| } |
| } |
| "".to_string() |
| } |
| |
| fn create_output_dir(path: &PathBuf) -> Result<(), Error> { |
| if path.exists() { |
| info!("Directory {} already exists", path.display()); |
| // Clear out the output folder |
| fs::remove_dir_all(path) |
| .with_context(|| format!("Unable to remove output directory {}", path.display()))?; |
| info!("Removed directory {}", path.display()); |
| } |
| |
| // Re-create output folder |
| fs::create_dir_all(path) |
| .with_context(|| format!("Unable to create output directory {}", path.display()))?; |
| info!("Created directory {}", path.display()); |
| |
| Ok(()) |
| } |
| |
| // Pre-processes the list of input files by removing duplicates. |
| fn normalize_input_files(input: &mut Vec<PathBuf>) { |
| input.sort_unstable(); |
| input.dedup(); |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use std::fs::File; |
| use std::io::Write; |
| use tempfile::{tempdir, NamedTempFile}; |
| |
| use std::path::PathBuf; |
| |
| use serde_json::Map; |
| |
| #[test] |
| fn select_template_test() { |
| let path = PathBuf::new(); |
| |
| let html_template = select_template(&TemplateType::HTML, &path).unwrap(); |
| assert_eq!(html_template.name(), "HTML".to_string()); |
| |
| let markdown_template = select_template(&TemplateType::Markdown, &path).unwrap(); |
| assert_eq!(markdown_template.name(), "Markdown".to_string()); |
| } |
| |
| #[test] |
| fn create_toc_test() { |
| let mut fidl_json_map: HashMap<String, FidlJson> = HashMap::new(); |
| fidl_json_map.insert( |
| "fuchsia.media".to_string(), |
| FidlJson { |
| name: "fuchsia.media".to_string(), |
| version: "0.0.1".to_string(), |
| maybe_attributes: Vec::new(), |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }, |
| ); |
| fidl_json_map.insert( |
| "fuchsia.auth".to_string(), |
| FidlJson { |
| name: "fuchsia.auth".to_string(), |
| version: "0.0.1".to_string(), |
| maybe_attributes: vec![json!({"name": "Doc", "value": "Fuchsia Auth API"})], |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }, |
| ); |
| fidl_json_map.insert( |
| "fuchsia.camera.common".to_string(), |
| FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: "0.0.1".to_string(), |
| maybe_attributes: vec![json!({"some_key": "key", "some_value": "not_description"})], |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }, |
| ); |
| |
| let toc = create_toc(&fidl_json_map); |
| assert_eq!(toc.len(), 3); |
| |
| let item0 = toc.get(0).unwrap(); |
| assert_eq!(item0.name, "fuchsia.auth".to_string()); |
| assert_eq!(item0.link, "fuchsia.auth/index".to_string()); |
| assert_eq!(item0.description, "Fuchsia Auth API".to_string()); |
| |
| let item1 = toc.get(1).unwrap(); |
| assert_eq!(item1.name, "fuchsia.camera.common".to_string()); |
| assert_eq!(item1.link, "fuchsia.camera.common/index".to_string()); |
| assert_eq!(item1.description, "".to_string()); |
| |
| let item2 = toc.get(2).unwrap(); |
| assert_eq!(item2.name, "fuchsia.media".to_string()); |
| assert_eq!(item2.link, "fuchsia.media/index".to_string()); |
| assert_eq!(item2.description, "".to_string()); |
| } |
| |
| #[test] |
| fn get_library_description_test() { |
| let maybe_attributes = vec![ |
| json!({"name": "Not Doc", "value": "Not the description"}), |
| json!({"name": "Doc", "value": "Fuchsia Auth API"}), |
| ]; |
| let description = get_library_description(&maybe_attributes); |
| assert_eq!(description, "Fuchsia Auth API".to_string()); |
| } |
| |
| #[test] |
| fn create_output_dir_test() { |
| // Create a temp dir to run tests on |
| let dir = tempdir().expect("Unable to create temp dir"); |
| let dir_path = PathBuf::from(dir.path()); |
| |
| // Add a temp file inside the temp dir |
| let file_path = dir_path.join("temp.txt"); |
| File::create(file_path).expect("Unable to create temp file"); |
| |
| create_output_dir(&dir_path).expect("create_output_dir failed"); |
| assert!(dir_path.exists()); |
| assert!(dir_path.is_dir()); |
| |
| // The temp file has been deleted |
| assert_eq!(dir_path.read_dir().unwrap().count(), 0); |
| } |
| |
| #[test] |
| fn get_fidldoc_config_default_path_test() { |
| // Ensure that I get a valid filepath |
| let default = std::env::current_exe().unwrap().parent().unwrap().join(FIDLDOC_CONFIG_PATH); |
| assert_eq!(default, get_fidldoc_config_default_path().unwrap()); |
| } |
| |
| #[test] |
| fn read_fidldoc_config_test() { |
| // Generate a test config file |
| let fidl_config_sample = json!({ |
| "title": "Fuchsia FIDLs" |
| }); |
| // Write this to a temporary file |
| let mut fidl_config_file = NamedTempFile::new().unwrap(); |
| fidl_config_file |
| .write(fidl_config_sample.to_string().as_bytes()) |
| .expect("Unable to write to temporary file"); |
| |
| // Read in file |
| let fidl_config = read_fidldoc_config(&fidl_config_file.path()).unwrap(); |
| assert_eq!(fidl_config["title"], "Fuchsia FIDLs".to_string()); |
| } |
| |
| #[test] |
| fn normalize_input_files_test() { |
| let mut input_files = vec![ |
| PathBuf::from(r"/tmp/file1"), |
| PathBuf::from(r"/file2"), |
| PathBuf::from(r"/usr/file1"), |
| ]; |
| normalize_input_files(&mut input_files); |
| assert_eq!(input_files.len(), 3); |
| |
| let mut dup_input_files = vec![ |
| PathBuf::from(r"/tmp/file1"), |
| PathBuf::from(r"/file2"), |
| PathBuf::from(r"/tmp/file1"), |
| ]; |
| normalize_input_files(&mut dup_input_files); |
| assert_eq!(dup_input_files.len(), 2); |
| } |
| |
| #[test] |
| fn should_process_test() { |
| let fidl_json = FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: SUPPORTED_FIDLJSON.to_string(), |
| maybe_attributes: vec![json!({"name": "not NoDoc", "value": ""})], |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }; |
| assert_eq!(should_process_fidl_json(&fidl_json), true); |
| } |
| |
| #[test] |
| fn check_version_test() { |
| let fidl_json = FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: "not a valid version string".to_string(), |
| maybe_attributes: vec![json!({"name": "not NoDoc", "value": ""})], |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }; |
| assert_eq!(should_process_fidl_json(&fidl_json), false); |
| } |
| |
| #[test] |
| fn check_nodoc_attribute_test() { |
| let fidl_json = FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: SUPPORTED_FIDLJSON.to_string(), |
| maybe_attributes: vec![json!({"name": "NoDoc", "value": ""})], |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: Vec::new(), |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }; |
| assert_eq!(should_process_fidl_json(&fidl_json), false); |
| } |
| |
| #[test] |
| fn check_documentation_ok() { |
| let fidl_json = FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: SUPPORTED_FIDLJSON.to_string(), |
| maybe_attributes: Vec::new(), |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: vec![json!({ |
| "name": "fuchsia.sysmem/HeapType", |
| "location": { |
| "filename": "../../sdk/fidl/fuchsia.sysmem/constraints.fidl", |
| "line": 216, |
| "column": 6, |
| "length": 8 |
| }, |
| "maybe_attributes": [ |
| { |
| "name": "Doc", |
| "value": "Device specific types should have bit 60 set.\n" |
| } |
| ]})], |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }; |
| check_documentation(&fidl_json).expect("Errors"); |
| } |
| |
| #[test] |
| fn check_documentation_nok() { |
| let fidl_json = FidlJson { |
| name: "fuchsia.camera.common".to_string(), |
| version: SUPPORTED_FIDLJSON.to_string(), |
| maybe_attributes: Vec::new(), |
| library_dependencies: Vec::new(), |
| bits_declarations: Vec::new(), |
| const_declarations: Vec::new(), |
| enum_declarations: vec![json!({ |
| "name": "fuchsia.sysmem/HeapType", |
| "location": { |
| "filename": "../../sdk/fidl/fuchsia.sysmem/constraints.fidl", |
| "line": 216, |
| "column": 6, |
| "length": 8 |
| }, |
| "maybe_attributes": [ |
| { |
| "name": "Doc", |
| "value": "Device specific types should have bit '60 set.\n" |
| } |
| ]})], |
| interface_declarations: Vec::new(), |
| table_declarations: Vec::new(), |
| type_alias_declarations: Vec::new(), |
| struct_declarations: Vec::new(), |
| union_declarations: Vec::new(), |
| declaration_order: Vec::new(), |
| declarations: Map::new(), |
| }; |
| let result = check_documentation(&fidl_json); |
| assert!(result.is_err()); |
| } |
| } |