From 96501bc1d33ce254d34b6dabb37803a92f103923 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sat, 4 Jan 2025 17:00:48 -0800 Subject: [PATCH] fix: Add coverage for blocks section (#171) --- src/tests/asciidoc_lang/blocks/delimited.rs | 4 - src/tests/asciidoc_lang/blocks/index.rs | 461 +++++++++++--------- 2 files changed, 253 insertions(+), 212 deletions(-) diff --git a/src/tests/asciidoc_lang/blocks/delimited.rs b/src/tests/asciidoc_lang/blocks/delimited.rs index 1d469515..79f8d284 100644 --- a/src/tests/asciidoc_lang/blocks/delimited.rs +++ b/src/tests/asciidoc_lang/blocks/delimited.rs @@ -843,10 +843,6 @@ Live within the simulated reality without want or fear. attrlist: None, }, )); - - // The delimiter length for the nested structural container can either - // be shorter or longer than the parent. That's a personal style - // choice. } non_normative!( diff --git a/src/tests/asciidoc_lang/blocks/index.rs b/src/tests/asciidoc_lang/blocks/index.rs index ca939029..591a19c0 100644 --- a/src/tests/asciidoc_lang/blocks/index.rs +++ b/src/tests/asciidoc_lang/blocks/index.rs @@ -55,40 +55,48 @@ The circumstance is determined by the context and style, and in the case of a de "# ); -// TO DO: Add coverage for remainder of this file. (May be redundant.) mod context { use std::ops::Deref; use crate::{ blocks::{Block, ContentModel, IsBlock}, + tests::sdd::{non_normative, to_do_verifies, verifies}, Span, }; - // == Context - // - // You may often hear a block referred to by a name, such as an example - // block, a sidebar block, an admonition block, or a section. That name - // is the block's context. + non_normative!( + r#" +== Context + +You may often hear a block referred to by a name, such as an example block, a sidebar block, an admonition block, or a section. +That name is the block's context. + +"# + ); #[test] fn section_context() { - // Let's consider the following normal section: - // - // ---- - // == Section Title - // - // Content of section. - // ---- - // - // The context of this block is `section`. We often refer to this as a section - // (or section block), using the context as an adjective to describe the block. - // The writer does not have to specify the context in this case since it's - // implied by the syntax. - // - // Every block has a context. The context is often implied by the syntax, but - // can be declared explicitly in certain cases. The context is what - // distinguishes one kind of block from another. You can think of the context as - // the block's type. + verifies!( + r#" +Let's consider the following normal section: + +---- +== Section Title + +Content of section. +---- + +The context of this block is `section`. +We often refer to this as a section (or section block), using the context as an adjective to describe the block. +The writer does not have to specify the context in this case since it's implied by the syntax. + +Every block has a context. +The context is often implied by the syntax, but can be declared explicitly in certain cases. +The context is what distinguishes one kind of block from another. +You can think of the context as the block's type. + +"# + ); let mi = Block::parse(Span::new("== Section Title\n\nContent of section.")) .unwrap_if_no_warnings() @@ -100,9 +108,13 @@ mod context { #[test] #[ignore] fn block_style() { - // The context can be further modified using a block style to create a - // family of blocks that share a common type, as is the case with - // admonition blocks and sections. We'll cover that modifier shortly. + non_normative!( + r#" +The context can be further modified using a block style to create a family of blocks that share a common type, as is the case with admonition blocks and sections. +We'll cover that modifier shortly. + +"# + ); todo!("Redundant: Covered by block_style test below."); } @@ -110,23 +122,30 @@ mod context { #[test] #[ignore] fn block_name() { - // For blocks, the context is sometimes referred to as the block name. - // This comes up in particular when talking about custom blocks. - // The block name is just another layer of abstraction. - // All the built-in block names map to exactly one context. - // But a block extension can map an arbitrary block name to one or more - // contexts. Which context is ultimately used depends on what is returned - // from the extension's process method. In the end, it's the context - // that determines how the block is converted. - - todo!("I don't understand block names. Will add test later."); + to_do_verifies!( + r#" +For blocks, the context is sometimes referred to as the block name. +This comes up in particular when talking about custom blocks. +The block name is just another layer of abstraction. +All the built-in block names map to exactly one context. +But a block extension can map an arbitrary block name to one or more contexts. +Which context is ultimately used depends on what is returned from the extension's process method. +In the end, it's the context that determines how the block is converted. + +"# + ); + + todo!("Revisit when we support block extensions."); } #[test] fn sections_are_compound() { - // The context often determines the content model. - // For example, all sections implicitly have the compound content model - // because a section may only contain other blocks. + verifies!( + r#" +The context often determines the content model. +For example, all sections implicitly have the compound content model because a section may only contain other blocks. +"# + ); let mi = Block::parse(Span::new("== Section Title\n\nContent of section.")) .unwrap_if_no_warnings() @@ -138,193 +157,219 @@ mod context { #[test] #[ignore] fn literal_blocks_are_verbatim() { - // All literal blocks - // implicitly have the verbatim content model because the purpose of - // this block is to present verbatim output. + verifies!( + r#" +All literal blocks implicitly have the verbatim content model because the purpose of this block is to present verbatim output. + +"# + ); + let mi = Block::parse(Span::new("....\nliteral text\n....")) + .unwrap_if_no_warnings() + .unwrap(); - todo!("Literal blocks aren't supported yet. Write test when ready."); + assert_eq!(mi.item.content_model(), ContentModel::Verbatim); } #[test] #[ignore] fn built_in_contexts() { - // === Summary of built-in contexts - // - // Here's a list of the contexts of all the built-in blocks in AsciiDoc. - // - // NOTE: In the Asciidoctor API, the contexts are represented as symbols. - // In Ruby, a symbol is a name prefixed with a colon (e.g., `:listing`). - // This documentation will sometimes use this notation when referring to the - // name of a context. However, this notation is not universal. - // Some processors, such as Asciidoctor.js, store the context as a string - // instead. - // - // .Built-in contexts - // [#table-of-contexts,cols="1s,2"] - // |=== - // |Name | Purpose - // - // |admonition - // |One of five admonition blocks. - // - // |audio - // |An audio block. - // - // |colist - // |A callout list. - // - // |dlist - // |A description list. - // - // |document - // |The top-level document or the document in an AsciiDoc table cell - // - // |example - // |An example block. - // - // |floating_title - // |A discrete heading. - // - // |image - // |An image block. - // - // |list_item - // |An item in an ordered, unordered, or description list (only relevant - // inside a list or description list block). In a description list, this - // block is used to represent the term and the description. - // - // |listing - // |A listing block. - // - // |literal - // |A literal block. - // - // |olist - // |An ordered list. - // - // |open - // |An open block. - // - // |page_break - // |A page break. - // - // |paragraph - // |A paragraph. - // - // |pass - // |A passthrough block. - // - // |preamble - // |The preamble of the document. - // - // |quote - // |A quote block (aka blockquote). - // - // |section - // |A section. - // May also be a part, chapter, or special section. - // - // |sidebar - // |A sidebar block. - // - // |table - // |A table block. - // - // |table_cell - // |A table cell (only relevant inside a table block). - // - // |thematic_break - // |A thematic break (aka horizontal rule). - // - // |toc - // |A TOC block (to designate custom TOC placement). - // - // |ulist - // |An unordered list. - // - // |verse - // |A verse block. - // - // |video - // |A video block. - // |=== - - todo!("Add coverage for built-in context types"); - } + non_normative!( + r#" +=== Summary of built-in contexts - #[test] - #[ignore] - fn inline_context() { - // NOTE: Each inline element also has a context, but those elements are not - // (yet) accessible from the parsed document model. +Here's a list of the contexts of all the built-in blocks in AsciiDoc. - todo!("Spec doesn't yet describe what inline contexts mean."); - } +NOTE: In the Asciidoctor API, the contexts are represented as symbols. +In Ruby, a symbol is a name prefixed with a colon (e.g., `:listing`). +This documentation will sometimes use this notation when referring to the name of a context. +However, this notation is not universal. +Some processors, such as Asciidoctor.js, store the context as a string instead. - #[test] - #[ignore] - fn additional_contexts_via_extensions() { - // Additional contexts may be introduced through the use of the block, block - // macro, or inline macro extension points. +.Built-in contexts +[#table-of-contexts,cols="1s,2"] +|=== +|Name | Purpose + +|admonition +|One of five admonition blocks. + +|audio +|An audio block. + +|colist +|A callout list. + +|dlist +|A description list. + +|document +|The top-level document or the document in an AsciiDoc table cell + +|example +|An example block. + +|floating_title +|A discrete heading. + +|image +|An image block. + +|list_item +|An item in an ordered, unordered, or description list (only relevant inside a list or description list block). +In a description list, this block is used to represent the term and the description. + +|listing +|A listing block. + +|literal +|A literal block. + +|olist +|An ordered list. + +|open +|An open block. + +|page_break +|A page break. + +|paragraph +|A paragraph. + +|pass +|A passthrough block. + +|preamble +|The preamble of the document. + +|quote +|A quote block (aka blockquote). + +|section +|A section. +May also be a part, chapter, or special section. + +|sidebar +|A sidebar block. + +|table +|A table block. - todo!("Extension points are not yet defined."); +|table_cell +|A table cell (only relevant inside a table block). + +|thematic_break +|A thematic break (aka horizontal rule). + +|toc +|A TOC block (to designate custom TOC placement). + +|ulist +|An unordered list. + +|verse +|A verse block. + +|video +|A video block. +|=== + +NOTE: Each inline element also has a context, but those elements are not (yet) accessible from the parsed document model. + +Additional contexts may be introduced through the use of the block, block macro, or inline macro extension points. + +"# + ); + + // Deemed non-normative because in asciidoc-parser, we don't provide a + // list of built-in contexts. (Context is a string, which is inherently + // extensible.) } #[test] fn contexts_used_by_converter() { - // === Contexts used by the converter - // - // The context is what the converter uses to dispatch to a convert - // method. The style is then used by the converter to apply - // special behavior to blocks of the same family. - // - // With two exceptions, there's a 1-to-1 mapping between the contexts - // and the handler methods of a converter. Those exceptions are - // the `list_item` and `table_cell` contexts, which are not - // mapped to a handler method. In the converter, these blocks - // must be accessed from their parent block. - - // NO-OP: This crate isn't a converter, so this part of the spec doesn't - // apply. + non_normative!( + r#" +=== Contexts used by the converter + +The context is what the converter uses to dispatch to a convert method. +The style is then used by the converter to apply special behavior to blocks of the same family. + +With two exceptions, there's a 1-to-1 mapping between the contexts and the handler methods of a converter. +Those exceptions are the `list_item` and `table_cell` contexts, which are not mapped to a handler method. +In the converter, these blocks must be accessed from their parent block. + +"# + ); + + // Treated as non-normative because asciidoc-parser is only a parser, + // not a converter. } } mod block_style { - // [#block-style] - // == Block style - // - // The context does not always tell the whole story of a block's identity. - // Some blocks require specialization. - // That's where the block style comes into play. - // - // Above some blocks, you may notice a name at the start of the block - // attribute list (e.g., `[source]` or `[verse]`). The first positional - // (unnamed) attribute in the block attribute list is used to declare - // the block style. - // - // The declared block style is the value the author supplies. - // That value is then interpreted and resolved. - // The resolved block style, if non-empty, specializes the block's context. - // (It may instead, or in addition to, alter the block's context). - // - // Consider the following example of a source block: - // - // [source] - // .... - // [source,ruby] - // ---- - // puts "Hello, World!" - // ---- - // .... - // - // The context of a source block is `listing` (as inferred from the block - // delimiters) and the style is `source` (as specified by the writer). - // We say that the style specializes the block as a source block. - // (Technically, the presence of a source language already implies the - // `source` style, but under the covers this is what's happening). The - // context of the block is still the same, but it has additional - // metadata to indicate that it requires special processing. - // + use crate::{ + blocks::{Block, IsBlock}, + tests::sdd::{non_normative, to_do_verifies}, + Span, + }; + + non_normative!( + r#" +[#block-style] +== Block style + +The context does not always tell the whole story of a block's identity. +Some blocks require specialization. +That's where the block style comes into play. + +Above some blocks, you may notice a name at the start of the block attribute list (e.g., `[source]` or `[verse]`). +The first positional (unnamed) attribute in the block attribute list is used to declare the block style. + +The declared block style is the value the author supplies. +That value is then interpreted and resolved. +The resolved block style, if non-empty, specializes the block's context. +(It may instead, or in addition to, alter the block's context). + +"# + ); + + #[test] + #[ignore] + fn source_block() { + to_do_verifies!( + r#" +Consider the following example of a source block: + +[source] +.... +[source,ruby] +---- +puts "Hello, World!" +---- +.... + +The context of a source block is `listing` (as inferred from the block delimiters) and the style is `source` (as specified by the writer). +We say that the style specializes the block as a source block. +(Technically, the presence of a source language already implies the `source` style, but under the covers this is what's happening). +The context of the block is still the same, but it has additional metadata to indicate that it requires special processing. + +"# + ); + + let mi = Block::parse(Span::new( + "[source,ruby]\n----\nputs \"Hello, World!\"\n----", + )) + .unwrap_if_no_warnings() + .unwrap(); + + assert_eq!(mi.item.context().as_ref(), "listing"); + // assert_eq!(mi.item.style(), "source"); + // assert_eq!(mi.item.content_model(), ContentModel::Verbatim); + } + + // TO DO: Cover the remainder ... + // We also see the block style used for other purposes. // The `appendix` block style (e.g., `[appendix]`) above the section title // specializes the section as an appendix (a special section) and thus has