first commit

This commit is contained in:
Tomas Dvorak
2026-02-22 10:42:17 +01:00
commit 55885a0e8f
239 changed files with 103690 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
package rustdocs
import (
"strings"
"testing"
"github.com/PuerkitoBio/goquery"
)
const testStructPageHTML = `
<!DOCTYPE html>
<html>
<body>
<main>
<div class="main-heading">
<div class="rustdoc-breadcrumbs"><a href="../index.html">std</a>::<wbr><a href="index.html">simd</a></div>
<h1>Struct <span class="struct">Mask</span></h1>
<span class="sub-heading"><span class="since" title="Stable since Rust version 1.0.0">1.0.0</span></span>
</div>
<pre class="rust item-decl"><code>pub struct Mask&lt;T, const N: usize&gt;(<span class="comment">/* private fields */</span>)</code></pre>
<details class="toggle top-doc" open="">
<div class="docblock"><p>A SIMD vector mask for N elements.</p></div>
</details>
<h2 id="implementations">Implementations</h2>
<details class="toggle implementors-toggle" open="">
<details class="toggle method-toggle" open="">
<section id="method.test" class="method">
<h4 class="code-header">pub fn <a href="#method.test" class="fn">test</a>(&amp;self, index: usize) -&gt; bool</h4>
</section>
<div class="docblock"><p>Tests the value of the specified element.</p></div>
</details>
</details>
</main>
</body>
</html>
`
const testSearchHTML = `
<div id="results">
<ul class="search-results active">
<a class="result-method" href="../std/simd/struct.Mask.html#method.test">
<span class="result-name">
<span class="typename">method</span>
<div class="path"><span>std::</span><span>simd::</span><span class="method">Mask::</span><span class="fn">test</span></div>
</span>
<div class="desc">Tests the value of the specified element.</div>
</a>
<a class="result-struct" href="../std/vec/struct.Vec.html">
<span class="result-name">
<span class="typename">struct</span>
<div class="path"><span>std::</span><span>vec::</span><span class="struct">Vec</span></div>
</span>
<div class="desc">A contiguous growable array type.</div>
</a>
<a class="result-fn" href="../std/io/fn.stdout.html">
<span class="result-name">
<span class="typename">fn</span>
<div class="path"><span>std::</span><span>io::</span><span class="fn">stdout</span></div>
</span>
<div class="desc">Constructs a new handle to the standard output.</div>
</a>
</ul>
</div>
`
const testCratePageHTML = `
<!DOCTYPE html>
<html>
<body>
<main>
<div class="main-heading">
<h1>Crate <span>serde</span></h1>
<span class="sub-heading"><span class="since">1.0.0</span></span>
</div>
<details class="toggle top-doc">
<div class="docblock"><p>A framework for serializing and deserializing Rust data structures.</p></div>
</details>
<h2 id="modules">Modules</h2>
<div class="item-table">
<div class="module-item"><a class="mod" href="de/index.html">de</a><div class="desc">Deserialize implementation.</div></div>
</div>
<h2 id="structs">Structs</h2>
<div class="item-table">
<div class="struct"><a class="struct" href="struct.Serializer.html">Serializer</a><div class="desc">A structure for serializing Rust values.</div></div>
</div>
<h2 id="enums">Enums</h2>
<div class="item-table">
<div class="enum"><a class="enum" href="enum.Error.html">Error</a><div class="desc">Errors during serialization.</div></div>
</div>
</main>
</body>
</html>
`
func TestParseItemPage(t *testing.T) {
parser := NewParser()
symbol, err := parser.ParseItemPage(testStructPageHTML, "https://docs.rs/std/simd/struct.Mask.html")
if err != nil {
t.Fatalf("ParseItemPage failed: %v", err)
}
if symbol.Name != "Mask" {
t.Errorf("Expected name 'Mask', got '%s'", symbol.Name)
}
if symbol.Kind != ItemKindStruct {
t.Errorf("Expected kind 'struct', got '%s'", symbol.Kind)
}
if symbol.Doc == "" {
t.Error("Expected non-empty doc")
}
if !strings.Contains(symbol.Signature, "struct Mask") {
t.Errorf("Expected signature to contain 'struct Mask', got '%s'", symbol.Signature)
}
}
func TestParseSearchResults(t *testing.T) {
parser := NewParser()
results, err := parser.ParseSearchResults(testSearchHTML)
if err != nil {
t.Fatalf("ParseSearchResults failed: %v", err)
}
if len(results) < 3 {
t.Fatalf("Expected at least 3 results, got %d", len(results))
}
method := results[0]
if method.Kind != "fn" {
t.Errorf("Expected kind 'fn' for method, got '%s'", method.Kind)
}
if method.Description == "" {
t.Error("Expected non-empty description")
}
structResult := results[1]
if structResult.Kind != "struct" {
t.Errorf("Expected kind 'struct', got '%s'", structResult.Kind)
}
fnResult := results[2]
if fnResult.Kind != "fn" {
t.Errorf("Expected kind 'fn', got '%s'", fnResult.Kind)
}
}
func TestParseCratePage(t *testing.T) {
parser := NewParser()
crate, err := parser.ParseCratePage(testCratePageHTML, "https://docs.rs/serde")
if err != nil {
t.Fatalf("ParseCratePage failed: %v", err)
}
if crate.Name == "" {
t.Error("Expected non-empty name")
}
if crate.Description == "" {
t.Error("Expected non-empty description")
}
}
func TestExtractKindFromClasses(t *testing.T) {
tests := []struct {
classes string
expected string
}{
{"result-struct", "struct"},
{"result-enum", "enum"},
{"result-trait", "trait"},
{"result-fn", "fn"},
{"result-macro", "macro"},
{"result-const", "const"},
{"result-static", "static"},
{"result-mod", "mod"},
{"result-method", "fn"},
{"result-externcrate", "mod"},
{"unknown-class", ""},
}
for _, tt := range tests {
t.Run(tt.classes, func(t *testing.T) {
got := extractKindFromClasses(tt.classes)
if got != tt.expected {
t.Errorf("extractKindFromClasses(%q) = %q, want %q", tt.classes, got, tt.expected)
}
})
}
}
func TestResolveURL(t *testing.T) {
tests := []struct {
base string
href string
expected string
}{
{"https://docs.rs", "/serde/struct.Serializer.html", "https://docs.rs/serde/struct.Serializer.html"},
{"https://docs.rs", "https://example.com/page", "https://example.com/page"},
}
for _, tt := range tests {
t.Run(tt.href, func(t *testing.T) {
got := resolveURL(tt.base, tt.href)
if got != tt.expected {
t.Errorf("resolveURL(%q, %q) = %q, want %q", tt.base, tt.href, got, tt.expected)
}
})
}
}
func TestCleanText(t *testing.T) {
tests := []struct {
input string
expected string
}{
{" hello world ", "hello world"},
{"single", "single"},
{"\n\ttabs\t\n", "tabs"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
if got := cleanText(tt.input); got != tt.expected {
t.Errorf("cleanText(%q) = %q, want %q", tt.input, got, tt.expected)
}
})
}
}
func TestExtractItemPath(t *testing.T) {
parser := NewParser()
html := `<div class="rustdoc-breadcrumbs"><a href="../index.html">std</a>::<a href="index.html">simd</a></div>`
doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
if err != nil {
t.Fatalf("Failed to parse HTML: %v", err)
}
got := parser.extractItemPath(doc, "https://docs.rs/std/simd/struct.Mask.html")
if !strings.Contains(got, "std") || !strings.Contains(got, "simd") {
t.Errorf("extractItemPath() = %q, expected to contain std and simd", got)
}
}
func TestExtractMethods(t *testing.T) {
parser := NewParser()
doc, err := goquery.NewDocumentFromReader(strings.NewReader(testStructPageHTML))
if err != nil {
t.Fatalf("Failed to parse HTML: %v", err)
}
methods := parser.ExtractMethods(doc)
if len(methods) == 0 {
t.Error("Expected at least one method")
return
}
if methods[0].Name == "" {
t.Error("Expected non-empty method name")
}
}