# Laboratorio de la semana 6: módulos, tipos opacos y pruebas por la interfaz

Los comandos se corren desde `code/week-06`, salvo cuando se indica otra carpeta. Las ideas
están en los apuntes, en la sección "El laboratorio, paso a paso".

Cada sesión está pensada para unos 90 minutos de trabajo. Lo marcado como **puede quedar
para antes de la siguiente sesión** es lo primero que se mueve si no alcanza el tiempo.

```sh
make lab        # compila cada parte con su Makefile
make test-lab   # comprueba las cuatro partes; falla hasta que terminen
```

Para ver solo una parte: `LAB_DIR=lab python3 -m unittest -v test_lab.SplitProgram`, y lo
mismo con `FractionModule`, `StackModule` y `OpaqueImage`.

Desde aquí, ninguna prueba hace `#include "algo.c"`. Las pruebas se compilan aparte y se
enlazan con el módulo.

## Sesión 1

### Parte 1: dividir un programa (unos 45 minutos). Carpeta `lab/split`

`market.c` lee `ventas.txt` y escribe una tabla y un resumen. Funciona, y está todo en un
solo archivo. **No lo borren:** las pruebas comparan su salida con la de sus módulos.

Primero léanlo, y respondan por escrito:

1. ¿Qué funciones escriben en la terminal y cuáles no?
2. ¿Qué funciones llama cada función? Dibújenlo con flechas.
3. ¿Qué tipo o constante necesita más de una parte del programa?

Después divídanlo en estos archivos, con estos nombres exactos:

| Archivo | Qué lleva |
|---|---|
| `sales.h`, `sales.c` | `struct sale`, `load_sales`, `sale_total`, `sales_total` y `best_sale`. `parse_line` es privada |
| `report.h`, `report.c` | `print_table` y `print_summary`. `print_money` es privada |
| `main.c` | `main`, y nada más |

No cambien los nombres ni los parámetros de las funciones, ni lo que escribe el programa.
Cada `.h` lleva guarda de inclusión y comentarios que dicen qué promete cada función.

Luego escriban `lab/split/Makefile`, tomando como modelo `receipt/Makefile`:

- `make` deja el ejecutable en `build/market` y los objetos en `build/main.o`,
  `build/sales.o` y `build/report.o`.
- Se compila con `-Wall -Wextra -std=c17 -g -fsanitize=address,undefined`, también al enlazar.
- Cada objeto depende de su `.c` y de todos los `.h` que incluye, directa o indirectamente.
- `make clean` borra `build/`.
- No pongan `@` delante de las recetas: las pruebas leen los comandos que `make` escribe.

Prueben a mano, desde `lab/split`, antes de correr las pruebas:

```sh
make && build/market
touch report.c && make     # ¿qué se volvió a compilar?
touch sales.h && make      # ¿y ahora? ¿por qué?
```

### Parte 2: fracciones con tipo opaco (unos 45 minutos). Carpeta `lab/fraction`

`fraction.h` dice lo que promete el módulo. No lo cambien. Escriban `fraction.c` y agreguen
pruebas en `test_fraction.c`, que solo pueden usar lo que dice el encabezado. Calculen a mano
cada resultado esperado.

Un consejo para el orden: primero `fraction_new`, porque ahí se reduce y se arregla el signo.
Después `fraction_add` y `fraction_multiply` son casi una línea cada una. `gcd` va como una
función `static`, porque es un detalle de este archivo.

```sh
cd lab/fraction && make && build/test_fraction
```

**Puede quedar para antes de la siguiente sesión:** `fraction_print` y las pruebas de
`fraction_equal`.

## Sesión 2

### Parte 3: dos implementaciones de `Stack` (unos 50 minutos). Carpeta `lab/stack`

`stack.h` y `test_stack.c` ya están hechos, y no se cambian. Léanlos primero. Escriban
`stack_array.c`, con un arreglo que crece con `realloc`, y luego `stack_blocks.c`, con
bloques enlazados. Los apuntes dibujan esta segunda representación. Antes de escribirla,
dibujen en papel qué pasa al meter el quinto valor y al sacarlo.

```sh
cd lab/stack && make
build/test_array
build/test_blocks
```

Fíjense en la salida de `make`: `test_stack.c` se compila una vez, y su objeto se enlaza dos.
Después respondan por escrito: ¿qué línea de `test_stack.c` tendrían que cambiar para pasar de
una implementación a la otra?

### Parte 4: el módulo de imágenes como tipo opaco (unos 40 minutos). Carpeta `lab/image`

Hoy `struct image` está en `image.h`, y `filters.c` y `filter_demo.c` miran sus campos. Háganlo
en este orden:

1. Lleven el `struct` de `image.h` a `image.c`, y pongan en `image.h`
   `typedef struct image Image;`. Compilen con `make` y **lean los errores**: esa es la lista
   de todo lo que dependía de la representación.
2. Agreguen a `image.h` y a `image.c`, con sus comentarios, estas funciones:

   ```c
   int image_width(const Image *img);
   int image_height(const Image *img);
   int image_max_value(const Image *img);
   int image_get(const Image *img, int x, int y);
   void image_set(Image *img, int x, int y, int value);
   ```

   Comenten qué exige cada una. `image_get` e `image_set` exigen que `x` e `y` estén dentro
   de la imagen.
3. Cambien `struct image *` por `Image *` en las firmas de `image.h` y de `filters.h`.
4. Reescriban `filters.c` y `filter_demo.c` usando solo la interfaz.

```sh
cd lab/image && make && build/filter_demo
make test_image && build/test_image
```

`test_image.c` no compila hasta que terminen el paso 2. Es lo esperado.

**Puede quedar para antes de la siguiente sesión:** reescribir `image_stats` y `filter_demo.c`,
y responder esto por escrito: si mañana cada fila de píxeles se guardara en su propio bloque de
memoria, ¿qué archivos cambiarían?
