internal/symbols/extract_test.go

e6cd75b5f28bacf51620bb531320c30fd4e66bfd
gitbay/internal/symbols/extract_test.go history · blame · raw

187 lines · 4838 bytes

3 symbols in this file
  1package symbols
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"testing"
  7)
  8
  9// got renders symbols as "kind name key line", one per entry, key left
 10// out when it equals the name.
 11func got(syms []Symbol) []string {
 12	var out []string
 13	for _, s := range syms {
 14		if s.Key != s.Name {
 15			out = append(out, fmt.Sprintf("%s %s %s %d", s.Kind, s.Name, s.Key, s.Line))
 16			continue
 17		}
 18		out = append(out, fmt.Sprintf("%s %s %d", s.Kind, s.Name, s.Line))
 19	}
 20	return out
 21}
 22
 23func TestExtract(t *testing.T) {
 24	tests := []struct {
 25		path string
 26		src  string
 27		want []string
 28	}{
 29		{"a.go", `package a
 30
 31// Doc.
 32func Top() {}
 33
 34type Server struct{}
 35
 36type (
 37	List[T any] struct{}
 38	ID int
 39)
 40
 41func (s *Server) Handle() {}
 42func (l List[T]) Push(v T) {}
 43func (List[T]) Len() int { return 0 }
 44
 45const Max = 3
 46const (
 47	A, B = 1, 2
 48	_    = 9
 49)
 50
 51var global int
 52`, []string{
 53			"function Top 4", "type Server 6", "type List 9", "type ID 10",
 54			"method Server.Handle Handle 13", "method List.Push Push 14", "method List.Len Len 15",
 55			"const Max 17", "const A 19", "const B 19", "var global 23",
 56		}},
 57		{"broken.go", "package a\n\nfunc Before() {}\n\nfunc (\n", []string{"function Before 3"}},
 58		{"gen.go", "// Code generated by stringer; DO NOT EDIT.\n\npackage a\n\nfunc X() {}\n", nil},
 59		{"a.swift", `import Foundation
 60
 61public final class Store {
 62    static func make() -> Store { Store() }
 63    class var shared: Store { make() }
 64}
 65struct Point { var x: Int }
 66enum Mode { case a }
 67protocol Drawable {}
 68actor Cache {}
 69typealias Handler = () -> Void
 70@MainActor func onMain() {}
 71`, []string{
 72			"class Store 3", "function make 4", "struct Point 7", "enum Mode 8",
 73			"interface Drawable 9", "class Cache 10", "type Handler 11", "function onMain 12",
 74		}},
 75		{"lib.rs", `pub fn open() {}
 76pub(crate) async fn fetch() {}
 77struct Inner;
 78pub enum Kind { A }
 79pub trait Store {}
 80type Result<T> = std::result::Result<T, Error>;
 81mod tests {}
 82pub const LIMIT: usize = 3;
 83static mut COUNT: u32 = 0;
 84macro_rules! bail {}
 85impl Store for Inner {}
 86`, []string{
 87			"function open 1", "function fetch 2", "struct Inner 3", "enum Kind 4",
 88			"interface Store 5", "type Result 6", "module tests 7", "const LIMIT 8",
 89			"const COUNT 9", "macro bail 10",
 90		}},
 91		{"app.py", `import os
 92
 93def main():
 94    pass
 95
 96class Client:
 97    async def get(self):
 98        return helper()
 99
100async def run():
101    pass
102`, []string{"function main 3", "class Client 6", "method get 7", "function run 10"}},
103		{"web.ts", `export function render() {}
104export default async function load() {}
105function* gen() {}
106export class View {}
107export interface Props {}
108export type ID = string;
109export type Map<K, V> = Record<K, V>;
110export const enum Color { Red }
111export const LIMIT = 10;
112const handler = () => {};
113  const inner = 1;
114`, []string{
115			"function render 1", "function load 2", "function gen 3", "class View 4",
116			"interface Props 5", "type ID 6", "type Map 7", "enum Color 8",
117			"const LIMIT 9", "const handler 10",
118		}},
119		{"api.h", `#ifndef API_H
120#define API_H
121#define MAX_LEN 64
122struct buffer {
123    int len;
124};
125typedef struct buffer buffer_t;
126typedef enum mode { MODE_A } mode_t;
127int buffer_open(const char *path);
128static inline void *buffer_data(buffer_t *b);
129typedef struct {
130    int x;
131} point;
132#endif
133`, []string{
134			"macro API_H 2", "macro MAX_LEN 3", "struct buffer 4", "type buffer_t 7",
135			"enum mode 8", "function buffer_open 9", "function buffer_data 10", "type point 13",
136		}},
137		{"deploy.sh", `#!/bin/sh
138build() {
139  go build ./...
140}
141function release {
142  build
143}
144ship_it ()
145{
146  release
147}
148`, []string{"function build 2", "function release 5", "function ship_it 8"}},
149		{"README.md", "# Title\n\nText\n\n```sh\n# not a heading\n```\n\n## Usage ##\n", []string{"section Title 1", "section Usage 9"}},
150		{"notes.org", "* Top\n#+begin_src sh\n* not a heading\n#+end_src\n** Child heading\n", []string{"section Top 1", "section Child heading 5"}},
151		{"data.json", `{"a": 1}`, nil},
152	}
153	for _, tc := range tests {
154		t.Run(tc.path, func(t *testing.T) {
155			g := got(Extract(tc.path, []byte(tc.src)))
156			if strings.Join(g, "\n") != strings.Join(tc.want, "\n") {
157				t.Errorf("got:\n%s\nwant:\n%s", strings.Join(g, "\n"), strings.Join(tc.want, "\n"))
158			}
159		})
160	}
161}
162
163func TestSkip(t *testing.T) {
164	tests := []struct {
165		path string
166		size int64
167		skip bool
168	}{
169		{"main.go", 100, false},
170		{"main.go", MaxFileBytes + 1, true},
171		{"main.go", MaxFileBytes, false},
172		{"vendor/x/y.go", 10, true},
173		{"web/node_modules/a/index.js", 10, true},
174		{"internal/vendors/y.go", 10, false},
175		{"zz_gen.go", 10, true},
176		{"api.pb.go", 10, true},
177		{"static/app.min.js", 10, true},
178		{"static/app.js", 10, false},
179		{"image.png", 10, true},
180		{"Makefile", 10, true},
181	}
182	for _, tc := range tests {
183		if got := Skip(tc.path, tc.size); got != tc.skip {
184			t.Errorf("Skip(%q, %d) = %v, want %v", tc.path, tc.size, got, tc.skip)
185		}
186	}
187}